diff --git a/.hgtags b/.hgtags index bff7cf994..aac74dfcf 100644 --- a/.hgtags +++ b/.hgtags @@ -3,3 +3,4 @@ 0dc81e76166c56aaae64da48b673b62155943aad packet-history-working 38099dd26e9467b8f49f8632f22789858149a6e7 release ns-3.0.3 5701e60bf01a8ac1308945e69001e0cc07948faf release ns-3.0.4 +08046b6aef37932507696a2f2f427b42d693781e release ns-3.0.5 diff --git a/AUTHORS b/AUTHORS index f36dad9ce..1cb4b0af7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,4 +3,5 @@ Gustavo Carneiro (gjc@inescporto.pt, gjcarneiro@gmail.com) Craig Dowell (craigdo@ee.washington.edu) Tom Henderson (tomhend@u.washington.edu) Mathieu Lacage (mathieu.lacage@sophia.inria.fr) +Emmanuelle Laprise (emmmanuelle.laprise@bluekazoo.ca) George F. Riley (riley@ece.gatech.edu) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 9b3b40b1b..85a5f2d6e 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -3,12 +3,16 @@ This file contains ns-3 release notes (most recent releases first). -Release 3.0.5 (2007/08/XX) +Release 3.0.5 (2007/08/15) ======================== - - Add CSMA/CD model (Emmanuelle Laprise) - - Modularize ipv4 routing support (Gustavo Carneiro) - - Add mobility framework and basic mobility models + - Refactoring to support win32-based unix environments (Cygwin, mingw) + - "Packet socket" for allowing applications to access NetDevices directly + - Generalized, polymorphic Address class + - Add CSMA NetDevice model (from Emmanuelle Laprise) + - Modularize IPv4 routing support (from Gustavo Carneiro) + - Add mobility framework and basic mobility models + - Global unicast centralized routing Release 3.0.4 (2007/07/15) ======================== diff --git a/VERSION b/VERSION index b0f2dcb32..eca690e73 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.0.4 +3.0.5 diff --git a/doc/doxygen.conf b/doc/doxygen.conf index d71b47412..f50859219 100644 --- a/doc/doxygen.conf +++ b/doc/doxygen.conf @@ -1002,7 +1002,7 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = RUN_SELF_TESTS NS3_DEBUG_ENABLE +PREDEFINED = RUN_SELF_TESTS NS3_DEBUG_ENABLE NS3_ASSERT_ENABLE # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/doc/release_steps.txt b/doc/release_steps.txt index 689013ce6..5fe843d77 100644 --- a/doc/release_steps.txt +++ b/doc/release_steps.txt @@ -5,23 +5,20 @@ Steps in doing an ns-3 release - revise and check in RELEASE_NOTES - update and check in VERSION to the latest release number 2. make a new "architecture.pdf" document and place it in the doc/ directory -3. add current version of waf script from subversion: - - svn checkout http://waf.googlecode.com/svn/tags/ns3/ waf - - build waf script and put it into top of ns-3-dev -4. cd ns-3-dev; ./waf configure; ./waf dist -5. test tarball on release platforms (run-tests and simple-p2p) -6. tag ns-3-dev with "release ns-3.0.X" +3. cd ns-3-dev; ./waf configure; ./waf dist +4. test tarball on release platforms (waf check and maybe some other scripts) +5. tag ns-3-dev with "release ns-3.0.X" - hg tag "release ns-3.0.x" - hg push -7. clone the tagged ns-3-dev and place it on the repository +6. clone the tagged ns-3-dev and place it on the repository - ssh code.nsnam.org; sudo; su code; - cp -r /home/code/repos/ns-3-dev /home/code/repos/ns-3.0.x - cd /home/code/repos/ns-3.0.x/.hg and edit the hgrc appropriately -8. upload "ns-3.0.x.tar.bz2" to the releases/ directory on the server -9. update web page +7. upload "ns-3.0.x.tar.bz2" to the releases/ directory on the server +8. update web page - add link to news.html - update download.html - update roadmap.html - build and update Doxygen directory on the server - update and upload software architecture document (PDF, HTML) -10. announce to ns-developers, with summary of release notes +9. announce to ns-developers, with summary of release notes diff --git a/examples/csma-cd-one-subnet.cc b/examples/csma-one-subnet.cc similarity index 73% rename from examples/csma-cd-one-subnet.cc rename to examples/csma-one-subnet.cc index caff83dcf..dc1bd58c0 100644 --- a/examples/csma-cd-one-subnet.cc +++ b/examples/csma-one-subnet.cc @@ -26,7 +26,7 @@ // - UDP packet size of 210 bytes, with per-packet interval 0.00375 sec. // (i.e., DataRate of 448,000 bps) // - DropTail queues -// - Tracing of queues and packet receptions to file "csma-cd-one-subnet.tr" +// - Tracing of queues and packet receptions to file "csma-one-subnet.tr" #include #include @@ -46,22 +46,18 @@ #include "ns3/ascii-trace.h" #include "ns3/pcap-trace.h" #include "ns3/internet-node.h" -#include "ns3/csma-cd-channel.h" -#include "ns3/csma-cd-net-device.h" -#include "ns3/csma-cd-topology.h" -#include "ns3/csma-cd-ipv4-topology.h" -#include "ns3/mac-address.h" +#include "ns3/csma-channel.h" +#include "ns3/csma-net-device.h" +#include "ns3/csma-topology.h" +#include "ns3/csma-ipv4-topology.h" +#include "ns3/eui48-address.h" #include "ns3/ipv4-address.h" +#include "ns3/inet-socket-address.h" #include "ns3/ipv4.h" #include "ns3/socket.h" #include "ns3/ipv4-route.h" #include "ns3/onoff-application.h" -#include "ns3/ascii-trace.h" - -#include "ns3/trace-context.h" -#include "ns3/trace-root.h" - using namespace ns3; @@ -72,11 +68,11 @@ int main (int argc, char *argv[]) // Users may find it convenient to turn on explicit debugging // for selected modules; the below lines suggest how to do this #if 0 - DebugComponentEnable("CsmaCdNetDevice"); + DebugComponentEnable("CsmaNetDevice"); DebugComponentEnable("Ipv4L3Protocol"); DebugComponentEnable("NetDevice"); DebugComponentEnable("Channel"); - DebugComponentEnable("CsmaCdChannel"); + DebugComponentEnable("CsmaChannel"); DebugComponentEnable("PacketSocket"); #endif @@ -86,7 +82,7 @@ int main (int argc, char *argv[]) // The below Bind command tells the queue factory which class to // instantiate, when the queue factory is invoked in the topology code - Bind ("Queue", "DropTailQueue"); + DefaultValue::Bind ("Queue", "DropTailQueue"); // Allow the user to override any of the defaults and the above // Bind()s at run-time, via command-line arguments @@ -100,30 +96,30 @@ int main (int argc, char *argv[]) Ptr n3 = Create (); // We create the channels first without any IP addressing information - Ptr channel0 = - CsmaCdTopology::CreateCsmaCdChannel( + Ptr channel0 = + CsmaTopology::CreateCsmaChannel( DataRate(5000000), MilliSeconds(2)); - uint32_t n0ifIndex = CsmaCdIpv4Topology::AddIpv4CsmaCdNode (n0, channel0, - MacAddress("10:54:23:54:23:50")); - uint32_t n1ifIndex = CsmaCdIpv4Topology::AddIpv4CsmaCdNode (n1, channel0, - MacAddress("10:54:23:54:23:51")); - uint32_t n2ifIndex = CsmaCdIpv4Topology::AddIpv4CsmaCdNode (n2, channel0, - MacAddress("10:54:23:54:23:52")); - uint32_t n3ifIndex = CsmaCdIpv4Topology::AddIpv4CsmaCdNode (n3, channel0, - MacAddress("10:54:23:54:23:53")); + uint32_t n0ifIndex = CsmaIpv4Topology::AddIpv4CsmaNode (n0, channel0, + Eui48Address("10:54:23:54:23:50")); + uint32_t n1ifIndex = CsmaIpv4Topology::AddIpv4CsmaNode (n1, channel0, + Eui48Address("10:54:23:54:23:51")); + uint32_t n2ifIndex = CsmaIpv4Topology::AddIpv4CsmaNode (n2, channel0, + Eui48Address("10:54:23:54:23:52")); + uint32_t n3ifIndex = CsmaIpv4Topology::AddIpv4CsmaNode (n3, channel0, + Eui48Address("10:54:23:54:23:53")); // Later, we add IP addresses. - CsmaCdIpv4Topology::AddIpv4Address ( + CsmaIpv4Topology::AddIpv4Address ( n0, n0ifIndex, Ipv4Address("10.1.1.1"), Ipv4Mask("255.255.255.0")); - CsmaCdIpv4Topology::AddIpv4Address ( + CsmaIpv4Topology::AddIpv4Address ( n1, n1ifIndex, Ipv4Address("10.1.1.2"), Ipv4Mask("255.255.255.0")); - CsmaCdIpv4Topology::AddIpv4Address ( + CsmaIpv4Topology::AddIpv4Address ( n2, n2ifIndex, Ipv4Address("10.1.1.3"), Ipv4Mask("255.255.255.0")); - CsmaCdIpv4Topology::AddIpv4Address ( + CsmaIpv4Topology::AddIpv4Address ( n3, n3ifIndex, Ipv4Address("10.1.1.4"), Ipv4Mask("255.255.255.0")); // Create the OnOff application to send UDP datagrams of size @@ -131,8 +127,7 @@ int main (int argc, char *argv[]) // from n0 to n1 Ptr ooff = Create ( n0, - Ipv4Address("10.1.1.2"), - 80, + InetSocketAddress ("10.1.1.2", 80), "Udp", ConstantVariable(1), ConstantVariable(0)); @@ -143,8 +138,7 @@ int main (int argc, char *argv[]) // Create a similar flow from n3 to n0, starting at time 1.1 seconds ooff = Create ( n3, - Ipv4Address("10.1.1.1"), - 80, + InetSocketAddress ("10.1.1.1", 80), "Udp", ConstantVariable(1), ConstantVariable(0)); @@ -153,8 +147,8 @@ int main (int argc, char *argv[]) ooff->Stop (Seconds(10.0)); // Configure tracing of all enqueue, dequeue, and NetDevice receive events - // Trace output will be sent to the csma-cd-one-subnet.tr file - AsciiTrace asciitrace ("csma-cd-one-subnet.tr"); + // Trace output will be sent to the csma-one-subnet.tr file + AsciiTrace asciitrace ("csma-one-subnet.tr"); asciitrace.TraceAllNetDeviceRx (); asciitrace.TraceAllQueues (); @@ -163,7 +157,7 @@ int main (int argc, char *argv[]) // simple-point-to-point.pcap-- // and can be read by the "tcpdump -r" command (use "-tt" option to // display timestamps correctly) - PcapTrace pcaptrace ("csma-cd-one-subnet.pcap"); + PcapTrace pcaptrace ("csma-one-subnet.pcap"); pcaptrace.TraceAllIp (); Simulator::Run (); diff --git a/examples/csma-packet-socket.cc b/examples/csma-packet-socket.cc new file mode 100644 index 000000000..9db207fbe --- /dev/null +++ b/examples/csma-packet-socket.cc @@ -0,0 +1,136 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +// Port of ns-2/tcl/ex/simple.tcl to ns-3 +// +// Network topology +// +// n0 n1 n2 n3 +// | | | | +// ===================== +// +// - CBR/UDP flows from n0 to n1, and from n3 to n0 +// - UDP packet size of 210 bytes, with per-packet interval 0.00375 sec. +// (i.e., DataRate of 448,000 bps) +// - DropTail queues +// - Tracing of queues and packet receptions to file "csma-one-subnet.tr" + +#include +#include +#include +#include + +#include "ns3/command-line.h" +#include "ns3/default-value.h" +#include "ns3/ptr.h" +#include "ns3/random-variable.h" +#include "ns3/debug.h" + +#include "ns3/simulator.h" +#include "ns3/nstime.h" +#include "ns3/data-rate.h" + +#include "ns3/ascii-trace.h" +#include "ns3/pcap-trace.h" +#include "ns3/internet-node.h" +#include "ns3/csma-channel.h" +#include "ns3/csma-net-device.h" +#include "ns3/eui48-address.h" +#include "ns3/packet-socket-address.h" +#include "ns3/socket.h" +#include "ns3/onoff-application.h" +#include "ns3/queue.h" + +using namespace ns3; + +static Ptr +CreateCsmaDevice (Ptr node, Ptr channel) +{ + Ptr device = Create (node); + device->Attach (channel); + Ptr queue = Queue::CreateDefault (); + device->AddQueue (queue); + return device; +} + + +int main (int argc, char *argv[]) +{ + CommandLine::Parse (argc, argv); + + // Here, we will explicitly create four nodes. In more sophisticated + // topologies, we could configure a node factory. + Ptr n0 = Create (); + Ptr n1 = Create (); + Ptr n2 = Create (); + Ptr n3 = Create (); + + // create the shared medium used by all csma devices. + Ptr channel = Create (DataRate(5000000), MilliSeconds(2)); + + // use a helper function to connect our nodes to the shared channel. + Ptr n0If = CreateCsmaDevice (n0, channel); + Ptr n1If = CreateCsmaDevice (n1, channel); + Ptr n2If = CreateCsmaDevice (n2, channel); + Ptr n3If = CreateCsmaDevice (n3, channel); + + + // create the address which identifies n1 from n0 + PacketSocketAddress n0ToN1; + n0ToN1.SetSingleDevice (n0If->GetIfIndex ()); // set outgoing interface for outgoing packets + n0ToN1.SetPhysicalAddress (n1If->GetAddress ()); // set destination address for outgoing packets + n0ToN1.SetProtocol (2); // set arbitrary protocol for outgoing packets + + // create the address which identifies n0 from n3 + PacketSocketAddress n3ToN0; + n3ToN0.SetSingleDevice (n3If->GetIfIndex ()); + n3ToN0.SetPhysicalAddress (n0If->GetAddress ()); + n3ToN0.SetProtocol (3); + + // Create the OnOff application to send raw datagrams of size + // 210 bytes at a rate of 448 Kb/s + // from n0 to n1 + Ptr ooff = Create ( + n0, + n0ToN1, + "Packet", + ConstantVariable(1), + ConstantVariable(0)); + // Start the application + ooff->Start(Seconds(1.0)); + ooff->Stop (Seconds(10.0)); + + // Create a similar flow from n3 to n0, starting at time 1.1 seconds + ooff = Create ( + n3, + n3ToN0, + "Packet", + ConstantVariable(1), + ConstantVariable(0)); + // Start the application + ooff->Start(Seconds(1.1)); + ooff->Stop (Seconds(10.0)); + + // Configure tracing of all enqueue, dequeue, and NetDevice receive events + // Trace output will be sent to the csma-packet-socket.tr file + AsciiTrace asciitrace ("csma-packet-socket.tr"); + asciitrace.TraceAllNetDeviceRx (); + asciitrace.TraceAllQueues (); + + Simulator::Run (); + + Simulator::Destroy (); +} diff --git a/examples/mixed-global-routing.cc b/examples/mixed-global-routing.cc new file mode 100644 index 000000000..b3017e029 --- /dev/null +++ b/examples/mixed-global-routing.cc @@ -0,0 +1,199 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// This script exercises global routing code in a mixed point-to-point +// and csma/cd environment +// +// Network topology +// +// n0 +// \ p-p +// \ (shared csma/cd) +// n2 -------------------------n3 +// / | | +// / p-p n4 n5 ---------- n6 +// n1 p-p +// +// - CBR/UDP flows from n0 to n6 +// - Tracing of queues and packet receptions to file "mixed-global-routing.tr" + +#include +#include +#include +#include + +#include "ns3/debug.h" + +#include "ns3/command-line.h" +#include "ns3/default-value.h" +#include "ns3/ptr.h" +#include "ns3/random-variable.h" + +#include "ns3/simulator.h" +#include "ns3/nstime.h" +#include "ns3/data-rate.h" + +#include "ns3/ascii-trace.h" +#include "ns3/pcap-trace.h" +#include "ns3/internet-node.h" +#include "ns3/point-to-point-channel.h" +#include "ns3/point-to-point-net-device.h" +#include "ns3/csma-channel.h" +#include "ns3/csma-net-device.h" +#include "ns3/csma-topology.h" +#include "ns3/csma-ipv4-topology.h" +#include "ns3/eui48-address.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv4.h" +#include "ns3/socket.h" +#include "ns3/inet-socket-address.h" +#include "ns3/ipv4-route.h" +#include "ns3/point-to-point-topology.h" +#include "ns3/onoff-application.h" +#include "ns3/global-route-manager.h" + +using namespace ns3; + +int main (int argc, char *argv[]) +{ + + // Users may find it convenient to turn on explicit debugging + // for selected modules; the below lines suggest how to do this +#if 0 + DebugComponentEnable ("Object"); + DebugComponentEnable ("Queue"); + DebugComponentEnable ("DropTailQueue"); + DebugComponentEnable ("Channel"); + DebugComponentEnable ("PointToPointChannel"); + DebugComponentEnable ("PointToPointNetDevice"); + DebugComponentEnable ("GlobalRouter"); + DebugComponentEnable ("GlobalRouteManager"); +#endif + + // Set up some default values for the simulation. Use the Bind () + // technique to tell the system what subclass of Queue to use, + // and what the queue limit is + + // The below DefaultValue::Bind command tells the queue factory which + // class to instantiate, when the queue factory is invoked in the + // topology code + DefaultValue::Bind ("Queue", "DropTailQueue"); + + DefaultValue::Bind ("OnOffApplicationPacketSize", "210"); + DefaultValue::Bind ("OnOffApplicationDataRate", "448kb/s"); + + //DefaultValue::Bind ("DropTailQueue::m_maxPackets", 30); + + // Allow the user to override any of the defaults and the above + // Bind ()s at run-time, via command-line arguments + CommandLine::Parse (argc, argv); + + Ptr n0 = Create (); + Ptr n1 = Create (); + Ptr n2 = Create (); + Ptr n3 = Create (); + Ptr n4 = Create (); + Ptr n5 = Create (); + Ptr n6 = Create (); + + // We create the channels first without any IP addressing information + Ptr channel0 = + PointToPointTopology::AddPointToPointLink ( + n0, n2, DataRate (5000000), MilliSeconds (2)); + + Ptr channel1 = + PointToPointTopology::AddPointToPointLink ( + n1, n2, DataRate (5000000), MilliSeconds (2)); + + Ptr channel2 = + PointToPointTopology::AddPointToPointLink ( + n5, n6, DataRate (1500000), MilliSeconds (10)); + + // We create the channels first without any IP addressing information + Ptr channelc0 = + CsmaTopology::CreateCsmaChannel( + DataRate(5000000), MilliSeconds(2)); + + uint32_t n2ifIndex = CsmaIpv4Topology::AddIpv4CsmaNode (n2, channelc0, + Eui48Address("10:54:23:54:23:50")); + uint32_t n3ifIndex = CsmaIpv4Topology::AddIpv4CsmaNode (n3, channelc0, + Eui48Address("10:54:23:54:23:51")); + uint32_t n4ifIndex = CsmaIpv4Topology::AddIpv4CsmaNode (n4, channelc0, + Eui48Address("10:54:23:54:23:52")); + uint32_t n5ifIndex = CsmaIpv4Topology::AddIpv4CsmaNode (n5, channelc0, + Eui48Address("10:54:23:54:23:53")); + + // Later, we add IP addresses. + PointToPointTopology::AddIpv4Addresses ( + channel0, n0, Ipv4Address ("10.1.1.1"), + n2, Ipv4Address ("10.1.1.2")); + + PointToPointTopology::AddIpv4Addresses ( + channel1, n1, Ipv4Address ("10.1.2.1"), + n2, Ipv4Address ("10.1.2.2")); + + PointToPointTopology::AddIpv4Addresses ( + channel2, n5, Ipv4Address ("10.1.3.1"), + n6, Ipv4Address ("10.1.3.2")); + + CsmaIpv4Topology::AddIpv4Address ( + n2, n2ifIndex, Ipv4Address("10.250.1.1"), Ipv4Mask("255.255.255.0")); + + CsmaIpv4Topology::AddIpv4Address ( + n3, n3ifIndex, Ipv4Address("10.250.1.2"), Ipv4Mask("255.255.255.0")); + + CsmaIpv4Topology::AddIpv4Address ( + n4, n4ifIndex, Ipv4Address("10.250.1.3"), Ipv4Mask("255.255.255.0")); + + CsmaIpv4Topology::AddIpv4Address ( + n5, n5ifIndex, Ipv4Address("10.250.1.4"), Ipv4Mask("255.255.255.0")); + + // Create router nodes, initialize routing database and set up the routing + // tables in the nodes. + GlobalRouteManager::PopulateRoutingTables (); + + // Create the OnOff application to send UDP datagrams of size + // 210 bytes at a rate of 448 Kb/s + Ptr ooff = Create ( + n0, + InetSocketAddress ("10.1.3.2", 80), + "Udp", + ConstantVariable (1), + ConstantVariable (0), + DataRate("300bps"), + 50); + // Start the application + ooff->Start (Seconds (1.0)); + ooff->Stop (Seconds (10.0)); + + // Configure tracing of all enqueue, dequeue, and NetDevice receive events + // Trace output will be sent to the simple-global-routing.tr file + AsciiTrace asciitrace ("mixed-global-routing.tr"); + asciitrace.TraceAllQueues (); + asciitrace.TraceAllNetDeviceRx (); + + // Also configure some tcpdump traces; each interface will be traced + // The output files will be named simple-p2p.pcap-- + // and can be read by the "tcpdump -r" command (use "-tt" option to + // display timestamps correctly) + PcapTrace pcaptrace ("mixed-global-routing.pcap"); + pcaptrace.TraceAllIp (); + + Simulator::Run (); + + Simulator::Destroy (); +} diff --git a/examples/simple-global-routing.cc b/examples/simple-global-routing.cc new file mode 100644 index 000000000..75879be23 --- /dev/null +++ b/examples/simple-global-routing.cc @@ -0,0 +1,183 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + * + * ns-2 simple.tcl script (ported from ns-2) + * Originally authored by Steve McCanne, 12/19/1996 + */ + +// Port of ns-2/tcl/ex/simple.tcl to ns-3 +// +// Network topology +// +// n0 +// \ 5 Mb/s, 2ms +// \ 1.5Mb/s, 10ms +// n2 -------------------------n3 +// / +// / 5 Mb/s, 2ms +// n1 +// +// - all links are point-to-point links with indicated one-way BW/delay +// - CBR/UDP flows from n0 to n3, and from n3 to n1 +// - FTP/TCP flow from n0 to n3, starting at time 1.2 to time 1.35 sec. +// - UDP packet size of 210 bytes, with per-packet interval 0.00375 sec. +// (i.e., DataRate of 448,000 bps) +// - DropTail queues +// - Tracing of queues and packet receptions to file "simple-global-routing.tr" + +#include +#include +#include +#include + +#include "ns3/debug.h" + +#include "ns3/command-line.h" +#include "ns3/default-value.h" +#include "ns3/ptr.h" +#include "ns3/random-variable.h" + +#include "ns3/simulator.h" +#include "ns3/nstime.h" +#include "ns3/data-rate.h" + +#include "ns3/ascii-trace.h" +#include "ns3/pcap-trace.h" +#include "ns3/internet-node.h" +#include "ns3/point-to-point-channel.h" +#include "ns3/point-to-point-net-device.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv4.h" +#include "ns3/socket.h" +#include "ns3/inet-socket-address.h" +#include "ns3/ipv4-route.h" +#include "ns3/point-to-point-topology.h" +#include "ns3/onoff-application.h" +#include "ns3/global-route-manager.h" + +using namespace ns3; + +int main (int argc, char *argv[]) +{ + + // Users may find it convenient to turn on explicit debugging + // for selected modules; the below lines suggest how to do this +#if 0 + DebugComponentEnable ("Object"); + DebugComponentEnable ("Queue"); + DebugComponentEnable ("DropTailQueue"); + DebugComponentEnable ("Channel"); + DebugComponentEnable ("PointToPointChannel"); + DebugComponentEnable ("PointToPointNetDevice"); + DebugComponentEnable ("GlobalRouter"); + DebugComponentEnable ("GlobalRouteMaager"); +#endif + + // Set up some default values for the simulation. Use the DefaultValue::Bind () + // technique to tell the system what subclass of Queue to use, + // and what the queue limit is + + // The below Bind command tells the queue factory which class to + // instantiate, when the queue factory is invoked in the topology code + DefaultValue::Bind ("Queue", "DropTailQueue"); + + DefaultValue::Bind ("OnOffApplicationPacketSize", "210"); + DefaultValue::Bind ("OnOffApplicationDataRate", "448kb/s"); + + //DefaultValue::Bind ("DropTailQueue::m_maxPackets", 30); + + // Allow the user to override any of the defaults and the above + // DefaultValue::Bind ()s at run-time, via command-line arguments + CommandLine::Parse (argc, argv); + + // Here, we will explicitly create four nodes. In more sophisticated + // topologies, we could configure a node factory. + Ptr n0 = Create (); + Ptr n1 = Create (); + Ptr n2 = Create (); + Ptr n3 = Create (); + + // We create the channels first without any IP addressing information + Ptr channel0 = + PointToPointTopology::AddPointToPointLink ( + n0, n2, DataRate (5000000), MilliSeconds (2)); + + Ptr channel1 = + PointToPointTopology::AddPointToPointLink ( + n1, n2, DataRate (5000000), MilliSeconds (2)); + + Ptr channel2 = + PointToPointTopology::AddPointToPointLink ( + n2, n3, DataRate (1500000), MilliSeconds (10)); + + // Later, we add IP addresses. + PointToPointTopology::AddIpv4Addresses ( + channel0, n0, Ipv4Address ("10.1.1.1"), + n2, Ipv4Address ("10.1.1.2")); + + PointToPointTopology::AddIpv4Addresses ( + channel1, n1, Ipv4Address ("10.1.2.1"), + n2, Ipv4Address ("10.1.2.2")); + + PointToPointTopology::AddIpv4Addresses ( + channel2, n2, Ipv4Address ("10.1.3.1"), + n3, Ipv4Address ("10.1.3.2")); + + // Create router nodes, initialize routing database and set up the routing + // tables in the nodes. + GlobalRouteManager::PopulateRoutingTables (); + + // Create the OnOff application to send UDP datagrams of size + // 210 bytes at a rate of 448 Kb/s + Ptr ooff = Create ( + n0, + InetSocketAddress ("10.1.3.2", 80), + "Udp", + ConstantVariable (1), + ConstantVariable (0)); + // Start the application + ooff->Start (Seconds (1.0)); + ooff->Stop (Seconds (10.0)); + + // Create a similar flow from n3 to n1, starting at time 1.1 seconds + ooff = Create ( + n3, + InetSocketAddress ("10.1.2.1", 80), + "Udp", + ConstantVariable (1), + ConstantVariable (0)); + // Start the application + ooff->Start (Seconds (1.1)); + ooff->Stop (Seconds (10.0)); + + // Configure tracing of all enqueue, dequeue, and NetDevice receive events + // Trace output will be sent to the simple-global-routing.tr file + AsciiTrace asciitrace ("simple-global-routing.tr"); + asciitrace.TraceAllQueues (); + asciitrace.TraceAllNetDeviceRx (); + + // Also configure some tcpdump traces; each interface will be traced + // The output files will be named simple-p2p.pcap-- + // and can be read by the "tcpdump -r" command (use "-tt" option to + // display timestamps correctly) + PcapTrace pcaptrace ("simple-global-routing.pcap"); + pcaptrace.TraceAllIp (); + + Simulator::Run (); + + Simulator::Destroy (); + + return 0; +} diff --git a/examples/simple-point-to-point.cc b/examples/simple-point-to-point.cc index cf3091a7b..4e7657cdb 100644 --- a/examples/simple-point-to-point.cc +++ b/examples/simple-point-to-point.cc @@ -57,8 +57,8 @@ #include "ns3/internet-node.h" #include "ns3/point-to-point-channel.h" #include "ns3/point-to-point-net-device.h" -#include "ns3/mac-address.h" #include "ns3/ipv4-address.h" +#include "ns3/inet-socket-address.h" #include "ns3/ipv4.h" #include "ns3/socket.h" #include "ns3/ipv4-route.h" @@ -72,6 +72,7 @@ int main (int argc, char *argv[]) // Users may find it convenient to turn on explicit debugging // for selected modules; the below lines suggest how to do this + // remember to add #include "ns3/debug.h" before enabling these #if 0 DebugComponentEnable("Object"); DebugComponentEnable("Queue"); @@ -87,12 +88,12 @@ int main (int argc, char *argv[]) // The below Bind command tells the queue factory which class to // instantiate, when the queue factory is invoked in the topology code - Bind ("Queue", "DropTailQueue"); + DefaultValue::Bind ("Queue", "DropTailQueue"); - Bind ("OnOffApplicationPacketSize", "210"); - Bind ("OnOffApplicationDataRate", "448kb/s"); + DefaultValue::Bind ("OnOffApplicationPacketSize", "210"); + DefaultValue::Bind ("OnOffApplicationDataRate", "448kb/s"); - //Bind ("DropTailQueue::m_maxPackets", 30); + //DefaultValue::Bind ("DropTailQueue::m_maxPackets", 30); // Allow the user to override any of the defaults and the above // Bind()s at run-time, via command-line arguments @@ -144,8 +145,7 @@ int main (int argc, char *argv[]) // 210 bytes at a rate of 448 Kb/s Ptr ooff = Create ( n0, - Ipv4Address("10.1.3.2"), - 80, + InetSocketAddress ("10.1.3.2", 80), "Udp", ConstantVariable(1), ConstantVariable(0)); @@ -156,8 +156,7 @@ int main (int argc, char *argv[]) // Create a similar flow from n3 to n1, starting at time 1.1 seconds ooff = Create ( n3, - Ipv4Address("10.1.2.1"), - 80, + InetSocketAddress ("10.1.2.1", 80), "Udp", ConstantVariable(1), ConstantVariable(0)); diff --git a/examples/wscript b/examples/wscript index 51d8eb824..a2268eb53 100644 --- a/examples/wscript +++ b/examples/wscript @@ -1,17 +1,27 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- -import Params def build(bld): - def create_ns_prog(name, source, deps=['core', 'common', 'simulator']): - obj = bld.create_obj('cpp', 'program') - obj.target = name - obj.uselib_local = ["ns3-%s" % dep for dep in deps] - obj.source = source - return obj - obj = create_ns_prog('simple-point-to-point', 'simple-point-to-point.cc', deps=['point-to-point', 'internet-node']) - obj = create_ns_prog('csma-cd-one-subnet', 'csma-cd-one-subnet.cc', deps=['csma-cd', 'internet-node']) + obj = bld.create_ns3_program('simple-global-routing', + ['point-to-point', 'internet-node', 'global-routing']) + obj.source = 'simple-global-routing.cc' - obj = create_ns_prog('simple-p2p-olsr', 'simple-p2p-olsr.cc', - deps=['point-to-point', 'internet-node', 'olsr']) + obj = bld.create_ns3_program('simple-point-to-point', + ['point-to-point', 'internet-node']) + obj.source = 'simple-point-to-point.cc' + obj = bld.create_ns3_program('csma-one-subnet', + ['csma', 'internet-node']) + obj.source = 'csma-one-subnet.cc' + + obj = bld.create_ns3_program('csma-packet-socket', + ['csma', 'internet-node']) + obj.source = 'csma-packet-socket.cc' + + obj = bld.create_ns3_program( 'mixed-global-routing', + ['point-to-point', 'internet-node', 'global-routing' , 'csma-cd']) + obj.source = 'mixed-global-routing.cc' + + obj = bld.create_ns3_program('simple-point-to-point-olsr', + ['point-to-point', 'internet-node', 'olsr']) + obj.source = 'simple-p2p-olsr.cc' diff --git a/samples/main-default-value.cc b/samples/main-default-value.cc index 014a7d18d..f125a7181 100644 --- a/samples/main-default-value.cc +++ b/samples/main-default-value.cc @@ -73,7 +73,7 @@ int main (int argc, char* argv[]) // global variable and value (string) to overwrite the default. // Here, the default value of 33 for testInt1 is overwritten with 57 // - Bind("testInt1", "57"); + DefaultValue::Bind("testInt1", "57"); TestClass* testclass = new TestClass (); NS_DEBUG_UNCOND("TestBool1 default value (" << testclass->m_testBool1 << ")"); diff --git a/samples/main-packet-header.cc b/samples/main-packet-header.cc new file mode 100644 index 000000000..df1b0a1bf --- /dev/null +++ b/samples/main-packet-header.cc @@ -0,0 +1,124 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +#include "ns3/packet.h" +#include "ns3/header.h" +#include + +using namespace ns3; + +/* A sample Header implementation + */ +class MyHeader : public Header +{ +public: + static uint32_t GetUid (void); + + MyHeader (); + virtual ~MyHeader (); + + void SetData (uint16_t data); + uint16_t GetData (void) const; + + std::string GetName (void) const; + void Print (std::ostream &os) const; + void Serialize (Buffer::Iterator start) const; + uint32_t Deserialize (Buffer::Iterator start); + uint32_t GetSerializedSize (void) const; +private: + uint16_t m_data; +}; + +MyHeader::MyHeader () +{ + // we must provide a public default constructor, + // implicit or explicit, but never private. +} +MyHeader::~MyHeader () +{} + +uint32_t +MyHeader::GetUid (void) +{ + // This string is used by the internals of the packet + // code to keep track of the packet metadata. + // You need to make sure that this string is absolutely + // unique. The code will detect any duplicate string. + static uint32_t uid = AllocateUid ("MyHeader.test.nsnam.org"); + return uid; +} + +std::string +MyHeader::GetName (void) const +{ + // This string is used to identify the type of + // my header by the packet printing routines. + return "MYHEADER"; +} +void +MyHeader::Print (std::ostream &os) const +{ + // This method is invoked by the packet printing + // routines to print the content of my header. + os << "data=" << m_data << std::endl; +} +uint32_t +MyHeader::GetSerializedSize (void) const +{ + // we reserve 2 bytes for our header. + return 2; +} +void +MyHeader::Serialize (Buffer::Iterator start) const +{ + // we can serialize two bytes at the start of the buffer. + // we write them in network byte order. + start.WriteHtonU16 (m_data); +} +uint32_t +MyHeader::Deserialize (Buffer::Iterator start) +{ + // we can deserialize two bytes from the start of the buffer. + // we read them in network byte order and store them + // in host byte order. + m_data = start.ReadNtohU16 (); + + // we return the number of bytes effectively read. + return 2; +} + +void +MyHeader::SetData (uint16_t data) +{ + m_data = data; +} +uint16_t +MyHeader::GetData (void) const +{ + return m_data; +} + + + +int main (int argc, char *argv[]) +{ + // instantiate a header. + MyHeader sourceHeader; + sourceHeader.SetData (2); + + // instantiate a packet + Packet p; + // and store my header into the packet. + p.AddHeader (sourceHeader); + + // print the content of my packet on the standard output. + p.Print (std::cout); + + // you can now remove the header from the packet: + MyHeader destinationHeader; + p.RemoveHeader (destinationHeader); + + // and check that the destination and source + // headers contain the same values. + NS_ASSERT (sourceHeader.GetData () == destinationHeader.GetData ()); + + return 0; +} diff --git a/samples/main-packet-printer.cc b/samples/main-packet-printer.cc index 771ae83b8..c840a13f0 100644 --- a/samples/main-packet-printer.cc +++ b/samples/main-packet-printer.cc @@ -90,15 +90,6 @@ void DefaultPrint (void) std::cout << std::endl; } -// The below functions are used in place of default versions, in the -// non-default case below. For instance, DoPrintIpv4Header will print -// out less IPv4 header information than the default print function -void -DoPrintDefault (std::ostream &os,uint32_t packetUid, uint32_t size, - std::string &name, struct PacketPrinter::FragmentInformation info) -{ - os << name <<" (size " << size << " trim_start " << info.start << " trim_end " << info.end << ")"; -} void DoPrintPayload (std::ostream & os,uint32_t packetUid,uint32_t size, struct PacketPrinter::FragmentInformation info) @@ -129,14 +120,10 @@ void NonDefaultPrint (void) // set a string separator automatically inserted // between each call to a printing function. printer.SetSeparator (" - "); - // set the default print function: invoked if no - // specialized function has been provided for a header - // or trailer - printer.AddDefaultPrinter (MakeCallback (&DoPrintDefault)); // set the payload print function - printer.AddPayloadPrinter (MakeCallback (&DoPrintPayload)); + printer.SetPayloadPrinter (MakeCallback (&DoPrintPayload)); // set the print function for the header type Ipv4Header. - printer.AddHeaderPrinter (MakeCallback (&DoPrintIpv4Header), + printer.SetHeaderPrinter (MakeCallback (&DoPrintIpv4Header), MakeCallback (&DoPrintIpv4HeaderFragment)); diff --git a/samples/main-packet-tag.cc b/samples/main-packet-tag.cc new file mode 100644 index 000000000..a23a8a904 --- /dev/null +++ b/samples/main-packet-tag.cc @@ -0,0 +1,136 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006,2007 INRIA + * + * 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: Mathieu Lacage + */ +#include "ns3/tag.h" +#include "ns3/packet.h" +#include + +using namespace ns3; + +// define this class in a public header +class MyTag : public Tag +{ +public: + // we have to define a public constructor + MyTag (); + // we have to define a public copy constructor + MyTag (const MyTag &other); + // we have to define a public destructor + ~MyTag (); + // we have to define a public static GetUid method + static uint32_t GetUid (void); + // we have to define a public Print method + void Print (std::ostream &os) const; + // we have to define a public GetSerializedSize method + uint32_t GetSerializedSize (void) const; + // we have to define a public Serialize method + void Serialize (Buffer::Iterator i) const; + // we have to define a public Deserialize method + uint32_t Deserialize (Buffer::Iterator i); + + // these are our accessors to our tag structure + void SetSimpleValue (uint8_t value); + uint8_t GetSimpleValue (void) const; +private: + uint8_t m_simpleValue; +}; + +MyTag::MyTag () +{} +MyTag::MyTag (const MyTag &other) + : m_simpleValue (other.m_simpleValue) +{} +MyTag::~MyTag () +{} +uint32_t +MyTag::GetUid (void) +{ + // we input a unique string to AllocateUid + // to avoid name collisions. + static uint32_t uid = AllocateUid ("MyTag.tests.nsnam.org"); + return uid; +} +void +MyTag::Print (std::ostream &os) const +{ + // print the content of this tag for Packet::PrintTags + os << "MyTag=0x" << std::hex << (uint32_t)m_simpleValue << std::dec; +} +uint32_t +MyTag::GetSerializedSize (void) const +{ + // we do not want to deal with parallel simulations + // so we return 0. + return 0; +} +void +MyTag::Serialize (Buffer::Iterator i) const +{ + // we will never be invoked because we are not doing + // parallel simulations so, we assert. + NS_ASSERT (false); +} +uint32_t +MyTag::Deserialize (Buffer::Iterator i) +{ + // we will never be invoked because we are not doing + // parallel simulations so, we assert. + NS_ASSERT (false); + // theoretically, return the number of bytes read + return 0; +} + + +void +MyTag::SetSimpleValue (uint8_t value) +{ + m_simpleValue = value; +} +uint8_t +MyTag::GetSimpleValue (void) const +{ + return m_simpleValue; +} + + +int main (int argc, char *argv[]) +{ + // create a tag. + MyTag tag; + tag.SetSimpleValue (0x56); + + // store the tag in a packet. + Packet p; + p.AddTag (tag); + + // create a copy of the packet + Packet aCopy = p; + + // read the tag from the packet copy + MyTag tagCopy; + p.PeekTag (tagCopy); + + // the copy and the original are the same ! + NS_ASSERT (tagCopy.GetSimpleValue () == tag.GetSimpleValue ()); + + aCopy.PrintTags (std::cout); + std::cout << std::endl; + + return 0; +} diff --git a/samples/main-packet.cc b/samples/main-packet.cc deleted file mode 100644 index 777f95815..000000000 --- a/samples/main-packet.cc +++ /dev/null @@ -1,103 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -#include "ns3/packet.h" -#include "ns3/header.h" -#include - -using namespace ns3; - -/* A sample Header implementation - */ -class MyHeader : public Header { -public: - MyHeader (); - virtual ~MyHeader (); - - void SetData (uint16_t data); - uint16_t GetData (void) const; -private: - virtual std::string DoGetName (void) const; - virtual void PrintTo (std::ostream &os) const; - virtual void SerializeTo (Buffer::Iterator start) const; - virtual uint32_t DeserializeFrom (Buffer::Iterator start); - virtual uint32_t GetSerializedSize (void) const; - - uint16_t m_data; -}; - -MyHeader::MyHeader () -{} -MyHeader::~MyHeader () -{} -std::string -MyHeader::DoGetName (void) const -{ - return "MyHeader"; -} -void -MyHeader::PrintTo (std::ostream &os) const -{ - os << "MyHeader data=" << m_data << std::endl; -} -uint32_t -MyHeader::GetSerializedSize (void) const -{ - return 2; -} -void -MyHeader::SerializeTo (Buffer::Iterator start) const -{ - // serialize in head of buffer - start.WriteHtonU16 (m_data); -} -uint32_t -MyHeader::DeserializeFrom (Buffer::Iterator start) -{ - // deserialize from head of buffer - m_data = start.ReadNtohU16 (); - return GetSerializedSize (); -} - -void -MyHeader::SetData (uint16_t data) -{ - m_data = data; -} -uint16_t -MyHeader::GetData (void) const -{ - return m_data; -} - -/* A sample Tag implementation - */ -struct MyTag { - uint16_t m_streamId; -}; - -static TagRegistration g_MyTagRegistration ("ns3::MyTag", 0); - - -static void -Receive (Packet p) -{ - MyHeader my; - p.RemoveHeader (my); - std::cout << "received data=" << my.GetData () << std::endl; - struct MyTag myTag; - p.PeekTag (myTag); -} - - -int main (int argc, char *argv[]) -{ - Packet p; - MyHeader my; - my.SetData (2); - std::cout << "send data=2" << std::endl; - p.AddHeader (my); - struct MyTag myTag; - myTag.m_streamId = 5; - p.AddTag (myTag); - Receive (p); - return 0; -} diff --git a/samples/main-random-topology.cc b/samples/main-random-topology.cc index 05f7f26c1..3fb52a0d9 100644 --- a/samples/main-random-topology.cc +++ b/samples/main-random-topology.cc @@ -24,12 +24,12 @@ CourseChange (Ptr position) int main (int argc, char *argv[]) { - Bind ("RandomDiscPositionX", "100"); - Bind ("RandomDiscPositionY", "50"); - Bind ("RandomDiscPositionRho", "Uniform:0:30"); + DefaultValue::Bind ("RandomDiscPositionX", "100"); + DefaultValue::Bind ("RandomDiscPositionY", "50"); + DefaultValue::Bind ("RandomDiscPositionRho", "Uniform:0:30"); - Bind ("RandomTopologyPositionType", "RandomDiscPosition"); - Bind ("RandomTopologyMobilityType", "StaticMobilityModel"); + DefaultValue::Bind ("RandomTopologyPositionType", "RandomDiscPosition"); + DefaultValue::Bind ("RandomTopologyMobilityType", "StaticMobilityModel"); CommandLine::Parse (argc, argv); diff --git a/samples/main-simple.cc b/samples/main-simple.cc index f0f4ce4dc..7285f4946 100644 --- a/samples/main-simple.cc +++ b/samples/main-simple.cc @@ -4,7 +4,9 @@ #include "ns3/simulator.h" #include "ns3/socket-factory.h" #include "ns3/socket.h" +#include "ns3/inet-socket-address.h" #include "ns3/nstime.h" +#include "ns3/packet.h" using namespace ns3; @@ -12,7 +14,7 @@ static void GenerateTraffic (Ptr socket, uint32_t size) { std::cout << "at=" << Simulator::Now ().GetSeconds () << "s, tx bytes=" << size << std::endl; - socket->Send (0, size); + socket->Send (Packet (size)); if (size > 0) { Simulator::Schedule (Seconds (0.5), &GenerateTraffic, socket, size - 50); @@ -24,15 +26,15 @@ GenerateTraffic (Ptr socket, uint32_t size) } static void -SocketPrinter (Ptr socket, uint32_t size, const Ipv4Address &from, uint16_t fromPort) +SocketPrinter (Ptr socket, const Packet &packet, const Address &from) { - std::cout << "at=" << Simulator::Now ().GetSeconds () << "s, rx bytes=" << size << std::endl; + std::cout << "at=" << Simulator::Now ().GetSeconds () << "s, rx bytes=" << packet.GetSize () << std::endl; } static void PrintTraffic (Ptr socket) { - socket->RecvDummy (MakeCallback (&SocketPrinter)); + socket->SetRecvCallback (MakeCallback (&SocketPrinter)); } void @@ -44,10 +46,12 @@ RunSimulation (void) Ptr socketFactory = a->QueryInterface (iid); Ptr sink = socketFactory->CreateSocket (); - sink->Bind (80); + InetSocketAddress local = InetSocketAddress (Ipv4Address::GetAny (), 80); + sink->Bind (local); Ptr source = socketFactory->CreateSocket (); - source->Connect (Ipv4Address::GetLoopback (), 80); + InetSocketAddress remote = InetSocketAddress (Ipv4Address::GetLoopback (), 80); + source->Connect (remote); GenerateTraffic (source, 500); PrintTraffic (sink); diff --git a/samples/wscript b/samples/wscript index 30c825555..d8733131a 100644 --- a/samples/wscript +++ b/samples/wscript @@ -1,28 +1,43 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- -import Params def build(bld): - def create_ns_prog(name, source, deps=['core', 'common', 'simulator']): - obj = bld.create_obj('cpp', 'program') - obj.target = name - obj.uselib_local = ["ns3-%s" % dep for dep in deps] - obj.source = source - return obj - - obj = create_ns_prog('main-debug', ['main-debug.cc', 'main-debug-other.cc']) - obj = create_ns_prog('main-callback', 'main-callback.cc') - obj = create_ns_prog('main-ptr', 'main-ptr.cc') - #obj = create_ns_prog('main-trace', 'main-trace.cc') - obj = create_ns_prog('main-simulator', 'main-simulator.cc') - obj = create_ns_prog('main-packet', 'main-packet.cc') - obj = create_ns_prog('main-test', 'main-test.cc') - obj = create_ns_prog('main-simple', 'main-simple.cc', - deps=['node', 'internet-node', 'applications']) - #obj = create_ns_prog('main-simple-p2p', 'main-simple-p2p.cc', deps=['node', 'point-to-point']) - obj = create_ns_prog('main-default-value', 'main-default-value.cc', - deps=['core', 'simulator', 'node', 'point-to-point']) - obj = create_ns_prog('main-grid-topology', 'main-grid-topology.cc', - deps=['core', 'simulator', 'mobility', 'internet-node']) - obj = create_ns_prog('main-random-topology', 'main-random-topology.cc', - deps=['core', 'simulator', 'mobility']) + obj = bld.create_ns3_program('main-debug') + obj.source = ['main-debug.cc', 'main-debug-other.cc'] + + obj = bld.create_ns3_program('main-callback') + obj.source = 'main-callback.cc' + + obj = bld.create_ns3_program('main-ptr') + obj.source = 'main-ptr.cc' + + obj = bld.create_ns3_program('main-simulator') + obj.source = 'main-simulator.cc' + + obj = bld.create_ns3_program('main-packet-header', ['common', 'simulator']) + obj.source = 'main-packet-header.cc' + + obj = bld.create_ns3_program('main-packet-tag', ['common', 'simulator']) + obj.source = 'main-packet-tag.cc' + + obj = bld.create_ns3_program('main-packet-printer', ['common', 'simulator', 'internet-node']) + obj.source = 'main-packet-printer.cc' + + obj = bld.create_ns3_program('main-test') + obj.source = 'main-test.cc' + + obj = bld.create_ns3_program('main-simple', + ['node', 'internet-node', 'applications']) + obj.source = 'main-simple.cc' + + obj = bld.create_ns3_program('main-default-value', + ['core', 'simulator', 'node', 'point-to-point']) + obj.source = 'main-default-value.cc' + + obj = bld.create_ns3_program('main-grid-topology', + ['core', 'simulator', 'mobility', 'internet-node']) + obj.source = 'main-grid-topology.cc' + + obj = bld.create_ns3_program('main-random-topology', + ['core', 'simulator', 'mobility']) + obj.source = 'main-random-topology.cc' diff --git a/src/applications/onoff-application.cc b/src/applications/onoff-application.cc index f9c4146c8..32bcd7564 100644 --- a/src/applications/onoff-application.cc +++ b/src/applications/onoff-application.cc @@ -22,7 +22,7 @@ // George F. Riley, Georgia Tech, Spring 2007 // Adapted from ApplicationOnOff in GTNetS. -#include "ns3/ipv4-address.h" +#include "ns3/address.h" #include "ns3/node.h" #include "ns3/nstime.h" #include "ns3/data-rate.h" @@ -31,6 +31,7 @@ #include "ns3/simulator.h" #include "ns3/socket-factory.h" #include "ns3/default-value.h" +#include "ns3/packet.h" #include "onoff-application.h" using namespace std; @@ -47,22 +48,20 @@ static NumericDefaultValue g_defaultSize ("OnOffApplicationPacketSize" // Constructors OnOffApplication::OnOffApplication(Ptr n, - const Ipv4Address rip, - uint16_t rport, + const Address &remote, std::string iid, const RandomVariable& ontime, const RandomVariable& offtime) : Application(n), m_cbrRate (g_defaultRate.GetValue ()) { - Construct (n, rip, rport, iid, + Construct (n, remote, iid, ontime, offtime, g_defaultSize.GetValue ()); } OnOffApplication::OnOffApplication(Ptr n, - const Ipv4Address rip, - uint16_t rport, + const Address &remote, std::string iid, const RandomVariable& ontime, const RandomVariable& offtime, @@ -71,22 +70,20 @@ OnOffApplication::OnOffApplication(Ptr n, : Application(n), m_cbrRate (rate) { - Construct (n, rip, rport, iid, + Construct (n, remote, iid, ontime, offtime, size); } void OnOffApplication::Construct (Ptr n, - const Ipv4Address rip, - uint16_t rport, + const Address &remote, std::string iid, const RandomVariable& onTime, const RandomVariable& offTime, uint32_t size) { m_socket = 0; - m_peerIp = rip; - m_peerPort = rport; + m_peer = remote; m_connected = false; m_onTime = onTime.Copy (); m_offTime = offTime.Copy (); @@ -144,7 +141,7 @@ void OnOffApplication::StartApplication() // Called at time specified by Star Ptr socketFactory = GetNode ()->QueryInterface (iid); m_socket = socketFactory->CreateSocket (); m_socket->Bind (); - m_socket->Connect (m_peerIp, m_peerPort); + m_socket->Connect (m_peer); } // Insure no pending event StopApplication(); @@ -209,7 +206,7 @@ void OnOffApplication::ScheduleStopEvent() void OnOffApplication::SendPacket() { NS_ASSERT (m_sendEvent.IsExpired ()); - m_socket->Send(0, m_pktSize); + m_socket->Send(Packet (m_pktSize)); m_totBytes += m_pktSize; m_lastStartTime = Simulator::Now(); m_residualBits = 0; diff --git a/src/applications/onoff-application.h b/src/applications/onoff-application.h index e3aafcb11..92dceb7f7 100644 --- a/src/applications/onoff-application.h +++ b/src/applications/onoff-application.h @@ -28,13 +28,13 @@ #include "ns3/application.h" #include "ns3/event-id.h" #include "ns3/ptr.h" +#include "ns3/data-rate.h" namespace ns3 { -class Ipv4Address; +class Address; class RandomVariable; class Socket; -class DataRate; /** * \brief Generate traffic to a single destination according to an @@ -52,23 +52,20 @@ class OnOffApplication : public Application public: /** * \param n node associated to this application - * \param rip remote ip address - * \param rport remove port number + * \param remote remote ip address * \param iid * \param ontime on time random variable * \param offtime off time random variable */ OnOffApplication(Ptr n, - const Ipv4Address rip, - uint16_t rport, + const Address &remote, std::string iid, const RandomVariable& ontime, const RandomVariable& offtime); /** * \param n node associated to this application - * \param rip remote ip address - * \param rport remove port number + * \param remote remote ip address * \param iid * \param ontime on time random variable * \param offtime off time random variable @@ -76,8 +73,7 @@ public: * \param size size of packets when sending data. */ OnOffApplication(Ptr n, - const Ipv4Address rip, - uint16_t rport, + const Address &remote, std::string iid, const RandomVariable& ontime, const RandomVariable& offtime, @@ -112,8 +108,7 @@ private: virtual void StopApplication (void); // Called at time specified by Stop void Construct (Ptr n, - const Ipv4Address rip, - uint16_t rport, + const Address &remote, std::string iid, const RandomVariable& ontime, const RandomVariable& offtime, @@ -126,8 +121,7 @@ private: void SendPacket(); Ptr m_socket; // Associated socket - Ipv4Address m_peerIp; // Peer IP address - uint16_t m_peerPort; // Peer port + Address m_peer; // Peer address bool m_connected; // True if connected RandomVariable* m_onTime; // rng for On Time RandomVariable* m_offTime; // rng for Off Time diff --git a/src/applications/wscript b/src/applications/wscript index 0874d58ab..02aa11f7f 100644 --- a/src/applications/wscript +++ b/src/applications/wscript @@ -1,10 +1,7 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- def build(bld): - obj = bld.create_obj('cpp', 'shlib') - obj.name = 'ns3-applications' - obj.target = obj.name - obj.uselib_local = ['ns3-node'] + obj = bld.create_ns3_module('applications', ['node']) obj.source = [ 'onoff-application.cc', ] diff --git a/src/common/array-trace-resolver.h b/src/common/array-trace-resolver.h index 572df6e57..e4507d4af 100644 --- a/src/common/array-trace-resolver.h +++ b/src/common/array-trace-resolver.h @@ -32,38 +32,10 @@ namespace ns3 { * \brief a helper class to offer trace resolution for an array of objects. * \ingroup lowleveltracing */ -template +template class ArrayTraceResolver : public TraceResolver { public: - /** - * \brief array index trace context - * - * During namespace parsing, ns3::ArrayTraceResolver will - * embed an instance of this class in the TraceContext - * associated to every child object of the object stored - * at the index. - * - * The reason why this class exists is to ensure that we - * need to ensure that we can store a unique type as context - * into the TraceContext associated to this trace resolver. - */ - class Index - { - public: - Index (); - Index (uint32_t index); - /** - * The Index is automatically convertible to the - * uin32_t type such that it really behaves like a uint32_t - * array index for the user. - * - * \returns the index itself - */ - operator uint32_t (); - private: - uint32_t m_index; - }; /** * \param context trace context associated to this trace resolver * \param getSize callback which returns dynamically the size of underlying array @@ -81,51 +53,37 @@ public: */ ArrayTraceResolver (TraceContext const &context, Callback getSize, - Callback get); + Callback get); private: virtual TraceResolverList DoLookup (std::string id) const; Callback m_getSize; - Callback m_get; + Callback m_get; }; }//namespace ns3 namespace ns3 { -template -ArrayTraceResolver::Index::Index () - : m_index () -{} -template -ArrayTraceResolver::Index::Index (uint32_t index) - : m_index (index) -{} -template -ArrayTraceResolver::Index::operator uint32_t () -{ - return m_index; -} - -template -ArrayTraceResolver::ArrayTraceResolver (TraceContext const &context, - Callback getSize, - Callback get) +template +ArrayTraceResolver::ArrayTraceResolver (TraceContext const &context, + Callback getSize, + Callback get) : TraceResolver (context), m_getSize (getSize), m_get (get) {} -template +template TraceResolver::TraceResolverList -ArrayTraceResolver::DoLookup (std::string id) const +ArrayTraceResolver::DoLookup (std::string id) const { TraceResolverList list; if (id == "*") { for (uint32_t i = 0; i < m_getSize (); i++) { - TraceContext context = GetContext (); - typename ArrayTraceResolver::Index index = typename ArrayTraceResolver::Index (i); - context.Add (index); - list.push_back (m_get (i)->CreateTraceResolver (context)); + TraceContext context = GetContext (); + INDEX index = i; + context.Add (index); + list.push_back (m_get (i)->CreateTraceResolver (context)); } } return list; diff --git a/src/common/buffer.cc b/src/common/buffer.cc index ef48b3704..196bb4889 100644 --- a/src/common/buffer.cc +++ b/src/common/buffer.cc @@ -410,8 +410,8 @@ Buffer::CreateFragment (uint32_t start, uint32_t length) const return tmp; } -void -Buffer::TransformIntoRealBuffer (void) const +Buffer +Buffer::CreateFullCopy (void) const { if (m_zeroAreaSize != 0) { @@ -428,8 +428,16 @@ Buffer::TransformIntoRealBuffer (void) const Buffer::Iterator i = tmp.End (); i.Prev (dataEnd); i.Write (m_data->m_data+m_data->m_initialStart,dataEnd); - *const_cast (this) = tmp; + return tmp; } + return *this; +} + +void +Buffer::TransformIntoRealBuffer (void) const +{ + Buffer tmp = CreateFullCopy (); + *const_cast (this) = tmp; } diff --git a/src/common/buffer.h b/src/common/buffer.h index d591bad72..229a040fd 100644 --- a/src/common/buffer.h +++ b/src/common/buffer.h @@ -320,7 +320,7 @@ public: */ inline Buffer::Iterator End (void) const; - void TransformIntoRealBuffer (void) const; + Buffer CreateFullCopy (void) const; inline Buffer (Buffer const &o); inline Buffer &operator = (Buffer const &o); @@ -343,6 +343,7 @@ private: }; inline uint8_t *GetStart (void) const; + void TransformIntoRealBuffer (void) const; static void Recycle (struct Buffer::BufferData *data); static struct Buffer::BufferData *Create (void); static struct Buffer::BufferData *Allocate (uint32_t size, uint32_t start); diff --git a/src/common/chunk-registry.cc b/src/common/chunk-registry.cc new file mode 100644 index 000000000..7186524ab --- /dev/null +++ b/src/common/chunk-registry.cc @@ -0,0 +1,117 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005 INRIA + * All rights reserved. + * + * 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: Mathieu Lacage + */ + +#include "chunk-registry.h" +#include "ns3/assert.h" + +namespace ns3 { + +ChunkRegistry::InfoVector * +ChunkRegistry::GetInfoVector (void) +{ + static InfoVector vec; + return &vec; +} + +std::string +ChunkRegistry::GetUidStringFromUid (uint32_t uid) +{ + InfoVector *vec = GetInfoVector (); + NS_ASSERT (uid >= 1 && uid <= vec->size ()); + Info info = (*vec)[uid - 1]; + return info.uidString; +} +uint32_t +ChunkRegistry::GetUidFromUidString (std::string uidString) +{ + uint32_t uid = 1; + InfoVector *vec = GetInfoVector (); + for (InfoVector::iterator i = vec->begin (); i != vec->end (); i++) + { + if (i->uidString == uidString) + { + return uid; + } + uid++; + } + NS_FATAL_ERROR ("Trying to access a non-registered Header or Trailer: \"" << uidString << "\". "<< + "You could try calling NS_HEADER_ENSURE_REGISTER somewhere."); + return 0; +} + +uint8_t * +ChunkRegistry::GetStaticInstance (uint32_t uid) +{ + InfoVector *vec = GetInfoVector (); + NS_ASSERT (uid >= 1 && uid <= vec->size ()); + Info info = (*vec)[uid - 1]; + return info.getStaticInstance (); +} +bool +ChunkRegistry::IsHeader (uint32_t uid) +{ + InfoVector *vec = GetInfoVector (); + NS_ASSERT (uid >= 1 && uid <= vec->size ()); + Info info = (*vec)[uid - 1]; + return info.isHeader; +} +bool +ChunkRegistry::IsTrailer (uint32_t uid) +{ + return !IsHeader (uid); +} +uint32_t +ChunkRegistry::Deserialize (uint32_t uid, uint8_t *instance, Buffer::Iterator i) +{ + InfoVector *vec = GetInfoVector (); + NS_ASSERT (uid >= 1 && uid <= vec->size ()); + Info info = (*vec)[uid - 1]; + return info.deserialize (instance, i); +} +void +ChunkRegistry::Print (uint32_t uid, uint8_t *instance, std::ostream &os) +{ + InfoVector *vec = GetInfoVector (); + NS_ASSERT (uid >= 1 && uid <= vec->size ()); + Info info = (*vec)[uid - 1]; + return info.print (instance, os); +} +std::string +ChunkRegistry::GetName (uint32_t uid, uint8_t *instance) +{ + InfoVector *vec = GetInfoVector (); + NS_ASSERT (uid >= 1 && uid <= vec->size ()); + Info info = (*vec)[uid - 1]; + return info.getName (instance); +} +void +ChunkRegistry::InvokePrintCallback (uint32_t uid, uint8_t *instance, std::ostream &os, + uint32_t packetUid, uint32_t size, + Ptr callback) +{ + InfoVector *vec = GetInfoVector (); + NS_ASSERT (uid >= 1 && uid <= vec->size ()); + Info info = (*vec)[uid - 1]; + info.invokePrintCallback (instance, os, packetUid, size, callback); +} + + +} // namespace ns3 diff --git a/src/common/chunk-registry.h b/src/common/chunk-registry.h new file mode 100644 index 000000000..34e3e42ec --- /dev/null +++ b/src/common/chunk-registry.h @@ -0,0 +1,180 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005 INRIA + * All rights reserved. + * + * 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: Mathieu Lacage + */ + +#ifndef CHUNK_REGISTRY_H +#define CHUNK_REGISTRY_H + +#include +#include +#include "buffer.h" +#include "ns3/ptr.h" +#include "ns3/callback.h" + +namespace ns3 { + +/** + * \brief this registry keeps track of all different + * types of headers and trailers and assigns to each of them + * a unique integer. + * \internal + */ +class ChunkRegistry +{ +public: + template + static uint32_t RegisterHeader (std::string uuid); + template + static uint32_t RegisterTrailer (std::string uuid); + + static std::string GetUidStringFromUid (uint32_t uid); + static uint32_t GetUidFromUidString (std::string uidString); + static uint8_t *GetStaticInstance (uint32_t uid); + static uint32_t Deserialize (uint32_t uid, uint8_t *instance, Buffer::Iterator i); + static void Print (uint32_t uid, uint8_t *instance, std::ostream &os); + static std::string GetName (uint32_t uid, uint8_t *instance); + static bool IsHeader (uint32_t uid); + static bool IsTrailer (uint32_t uid); + static void InvokePrintCallback (uint32_t uid, uint8_t *instance, std::ostream &os, + uint32_t packetUid, uint32_t size, + Ptr callback); +private: + typedef uint8_t *(*GetStaticInstanceCb) (void); + typedef uint32_t (*DeserializeCb) (uint8_t *, Buffer::Iterator); + typedef void (*PrintCb) (uint8_t *,std::ostream &); + typedef std::string (*GetNameCb) (uint8_t *); + typedef void (*InvokePrintCallbackCb) (uint8_t *instance, std::ostream &os, + uint32_t packetUid, uint32_t size, + Ptr callback); + struct Info { + std::string uidString; + bool isHeader; + GetStaticInstanceCb getStaticInstance; + DeserializeCb deserialize; + PrintCb print; + GetNameCb getName; + InvokePrintCallbackCb invokePrintCallback; + }; + typedef std::vector InfoVector; + static InfoVector *GetInfoVector (void); + template + static uint8_t *DoGetStaticInstance (void); + template + static uint32_t DoDeserialize (uint8_t *instance, Buffer::Iterator i); + template + static void DoPrint (uint8_t *instance, std::ostream &os); + template + static std::string DoGetName (uint8_t *instance); + template + static void DoInvokePrintCallback (uint8_t *instance, std::ostream &os, + uint32_t packetUid, uint32_t size, + Ptr callback); + template + static uint32_t GetUid (bool isHeader, std::string uidString); + +}; + + +} // namespace ns3 + +namespace ns3 { + +template +uint32_t +ChunkRegistry::RegisterHeader (std::string uuid) +{ + return GetUid (true, uuid); +} +template +uint32_t +ChunkRegistry::RegisterTrailer (std::string uuid) +{ + return GetUid (false, uuid); +} + +template +uint32_t +ChunkRegistry::GetUid (bool isHeader, std::string uidString) +{ + InfoVector *vec = GetInfoVector (); + uint32_t uid = 1; + for (InfoVector::iterator i = vec->begin (); i != vec->end (); i++) + { + if (i->uidString == uidString) + { + return uid; + } + uid++; + } + Info info; + info.getStaticInstance = &ChunkRegistry::DoGetStaticInstance; + info.print = &ChunkRegistry::DoPrint; + info.getName = &ChunkRegistry::DoGetName; + info.deserialize = &ChunkRegistry::DoDeserialize; + info.invokePrintCallback = &ChunkRegistry::DoInvokePrintCallback; + info.uidString = uidString; + info.isHeader = isHeader; + vec->push_back (info); + return vec->size (); +} + +template +uint8_t * +ChunkRegistry::DoGetStaticInstance () +{ + static T instance; + return reinterpret_cast (&instance); +} +template +uint32_t +ChunkRegistry::DoDeserialize (uint8_t *instance, Buffer::Iterator i) +{ + T *obj = reinterpret_cast (instance); + return obj->Deserialize (i); +} +template +void +ChunkRegistry::DoPrint (uint8_t *instance, std::ostream &os) +{ + T *obj = reinterpret_cast (instance); + obj->Print (os); +} +template +std::string +ChunkRegistry::DoGetName (uint8_t *instance) +{ + T *obj = reinterpret_cast (instance); + return obj->GetName (); +} +template +void +ChunkRegistry::DoInvokePrintCallback (uint8_t *instance, std::ostream &os, + uint32_t packetUid, uint32_t size, + Ptr callback) +{ + T *obj = reinterpret_cast (instance); + Callback cb; + cb.Assign (callback); + cb (os, packetUid, size, obj); +} + +} // namespace ns3 + +#endif /* CHUNK_H */ diff --git a/src/common/chunk.cc b/src/common/chunk.cc deleted file mode 100644 index 52c1112c3..000000000 --- a/src/common/chunk.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2005 INRIA - * All rights reserved. - * - * 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: Mathieu Lacage - */ - -#include "chunk.h" -#include "ns3/assert.h" - -namespace ns3 { - -Chunk::Chunk () -{} - -Chunk::~Chunk () -{} - -std::string -Chunk::GetName (void) const -{ - return DoGetName (); -} -void -Chunk::Print (std::ostream &os) const -{ - PrintTo (os); -} -uint32_t -Chunk::GetSize (void) const -{ - return GetSerializedSize (); -} -void -Chunk::Serialize (Buffer::Iterator start) const -{ - SerializeTo (start); -} -uint32_t -Chunk::Deserialize (Buffer::Iterator start) -{ - uint32_t deserialized = DeserializeFrom (start); - return deserialized; -} -std::ostream& operator<< (std::ostream& os, Chunk const& chunk) -{ - chunk.Print (os); - return os; -} - -}; // namespace ns3 diff --git a/src/common/chunk.h b/src/common/chunk.h deleted file mode 100644 index ab568cd9f..000000000 --- a/src/common/chunk.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2005 INRIA - * All rights reserved. - * - * 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: Mathieu Lacage - */ - -#ifndef CHUNK_H -#define CHUNK_H - -#include -#include -#include "buffer.h" - -namespace ns3 { - -class Chunk { -public: - Chunk (); - virtual ~Chunk (); - - std::string GetName (void) const; - void Print (std::ostream &os) const; - uint32_t GetSize (void) const; - void Serialize (Buffer::Iterator start) const; - uint32_t Deserialize (Buffer::Iterator start); -private: - virtual std::string DoGetName (void) const = 0; - virtual void PrintTo (std::ostream &os) const = 0; - virtual uint32_t GetSerializedSize (void) const = 0; - virtual void SerializeTo (Buffer::Iterator i) const = 0; - virtual uint32_t DeserializeFrom (Buffer::Iterator i) = 0; -}; - -std::ostream& operator<< (std::ostream& os, Chunk const& chunk); - -}; // namespace ns3 - -#endif /* CHUNK_H */ diff --git a/src/common/composite-trace-resolver.cc b/src/common/composite-trace-resolver.cc index 2fef9080b..bf60aa81e 100644 --- a/src/common/composite-trace-resolver.cc +++ b/src/common/composite-trace-resolver.cc @@ -29,6 +29,14 @@ CompositeTraceResolver::CompositeTraceResolver (TraceContext const &context) CompositeTraceResolver::~CompositeTraceResolver () {} +void +CompositeTraceResolver::Add (std::string name, + Callback createResolver) +{ + TraceContext traceContext = GetContext (); + DoAdd (name, createResolver, traceContext); +} + void CompositeTraceResolver::DoAdd (std::string name, Callback createResolver, @@ -107,20 +115,53 @@ CompositeTraceResolver::DoLookup (std::string id) const #ifdef RUN_SELF_TESTS #include "ns3/test.h" +#include "trace-context-element.h" namespace ns3 { +class TraceSourceTest : public TraceContextElement +{ +public: + enum Sources { + DOUBLEA, + DOUBLEB, + SUBRESOLVER, + }; + static uint16_t GetUid (void) + {static uint16_t uid = AllocateUid ("TraceSourceTest"); return uid;} + void Print (std::ostream &os) + {os << "tracesource="; + if (m_sources == DOUBLEA) {os << "doubleA";} + else if (m_sources == DOUBLEB) {os << "doubleB";} + else if (m_sources == SUBRESOLVER) {os << "subresolver";} + } + TraceSourceTest () : m_sources (TraceSourceTest::DOUBLEA) {} + TraceSourceTest (enum Sources sources) :m_sources (sources) {} + bool IsDoubleA (void) {return m_sources == TraceSourceTest::DOUBLEA;} + bool IsDoubleB (void) {return m_sources == TraceSourceTest::DOUBLEB;} +private: + enum TraceSourceTest::Sources m_sources; +}; + +class SubTraceSourceTest : public TraceContextElement +{ +public: + enum Sources { + INT, + }; + static uint16_t GetUid (void) + {static uint16_t uid = AllocateUid ("SubTraceSourceTest"); return uid;} + void Print (std::ostream &os) + {os << "subtracesource=int";} + SubTraceSourceTest () : m_sources (SubTraceSourceTest::INT) {} + SubTraceSourceTest (enum Sources sources) : m_sources (sources) {} +private: + enum Sources m_sources; +}; + class CompositeTraceResolverTest : public Test { public: - enum TraceSources { - TEST_TRACE_DOUBLEA, - TEST_TRACE_DOUBLEB, - TEST_TRACE_SUBRESOLVER, - }; - enum SubTraceSources { - TEST_SUBTRACE_INT, - }; CompositeTraceResolverTest (); virtual ~CompositeTraceResolverTest (); virtual bool RunTests (void); @@ -144,19 +185,19 @@ CompositeTraceResolverTest::~CompositeTraceResolverTest () void CompositeTraceResolverTest::TraceDouble (TraceContext const &context, double v) { - enum CompositeTraceResolverTest::TraceSources source; + TraceSourceTest source; context.Get (source); - switch (source) + if (source.IsDoubleA ()) { - case TEST_TRACE_DOUBLEA: m_gotDoubleA = true; - break; - case TEST_TRACE_DOUBLEB: + } + else if (source.IsDoubleB ()) + { m_gotDoubleB = true; - break; - default: + } + else + { NS_FATAL_ERROR ("should not get any other trace source in this sink"); - break; } } @@ -171,7 +212,8 @@ TraceResolver * CompositeTraceResolverTest::CreateSubResolver (TraceContext const &context) { CompositeTraceResolver *subresolver = new CompositeTraceResolver (context); - subresolver->Add ("trace-int", m_traceInt, TEST_SUBTRACE_INT); + subresolver->Add ("trace-int", m_traceInt, + SubTraceSourceTest (SubTraceSourceTest::INT)); return subresolver; } bool @@ -185,8 +227,10 @@ CompositeTraceResolverTest::RunTests (void) CompositeTraceResolver resolver (context) ; - resolver.Add ("trace-double-a", traceDoubleA, TEST_TRACE_DOUBLEA); - resolver.Add ("trace-double-b", traceDoubleB, TEST_TRACE_DOUBLEB); + resolver.Add ("trace-double-a", traceDoubleA, + TraceSourceTest (TraceSourceTest::DOUBLEA)); + resolver.Add ("trace-double-b", traceDoubleB, + TraceSourceTest (TraceSourceTest::DOUBLEB)); resolver.Connect ("/*", MakeCallback (&CompositeTraceResolverTest::TraceDouble, this)); @@ -279,7 +323,7 @@ CompositeTraceResolverTest::RunTests (void) resolver.Add ("subresolver", MakeCallback (&CompositeTraceResolverTest::CreateSubResolver, this), - TEST_TRACE_SUBRESOLVER); + TraceSourceTest (TraceSourceTest::SUBRESOLVER)); resolver.Connect ("/subresolver/trace-int", MakeCallback (&CompositeTraceResolverTest::TraceInt, this)); diff --git a/src/common/composite-trace-resolver.h b/src/common/composite-trace-resolver.h index a9603c609..cadf9da2d 100644 --- a/src/common/composite-trace-resolver.h +++ b/src/common/composite-trace-resolver.h @@ -111,6 +111,18 @@ public: void Add (std::string name, Callback createResolver, T const &context); + + /** + * \param name name of child trace resolver + * \param createResolver a trace resolver constructor + * + * Add a child trace resolver to this resolver. This child + * trace resolver will match the name specified during + * namespace resolution. When this happens, the constructor + * will be invoked to create the child trace resolver. + */ + void Add (std::string name, + Callback createResolver); private: template void DoAddTraceSource (std::string name, @@ -205,7 +217,6 @@ CompositeTraceResolver::Add (std::string name, DoAdd (name, createResolver, traceContext); } - }//namespace ns3 #endif /* COMPOSITE_TRACE_RESOLVER_H */ diff --git a/src/common/header.h b/src/common/header.h index 609729b26..c983dd18d 100644 --- a/src/common/header.h +++ b/src/common/header.h @@ -22,7 +22,29 @@ #ifndef HEADER_H #define HEADER_H -#include "chunk.h" +#include "chunk-registry.h" + +/** + * \relates ns3::Header + * \brief this macro should be instantiated exactly once for each + * new type of Header + * + * This macro will ensure that your new Header type is registered + * within the packet header registry. In most cases, this macro + * is not really needed but, for safety, please, use it all the + * time. + * + * Note: This macro is _absolutely_ needed if you try to run a + * distributed simulation. + */ +#define NS_HEADER_ENSURE_REGISTERED(x) \ +static class thisisaveryverylongclassname ##x \ +{ \ + public: \ + thisisaveryverylongclassname ##x () \ + { uint32_t uid; uid = x::GetUid ();} \ +} g_thisisanotherveryveryverylongname ## x; + namespace ns3 { @@ -30,68 +52,64 @@ namespace ns3 { * \brief Protocol header serialization and deserialization. * * Every Protocol header which needs to be inserted or removed - * from a Packet instance must derive from this abstract base class - * and implement the private pure virtual methods listed below: - * - ns3::Header::SerializeTo - * - ns3::Header::DeserializeFrom - * - ns3::Header::GetSerializedSize - * - ns3::Header::PrintTo + * from a Packet instance must derive from this base class and + * implement the following public methods: + * - a default constructor: is used by the internal implementation + * if the Packet class. + * - a static method named GetUid: is used to uniquely identify + * the type of each header. This method shall return a unique + * integer allocated with Header::AllocateUid. + * - a method named Serialize: is used by Packet::AddHeader to + * store a header into the byte buffer of a packet. + * The input iterator points to the start of the byte buffer in + * which the header should write its data. The data written + * is expected to match bit-for-bit the representation of this + * header in a real network. + * - a method named GetSerializedSize: is used by Packet::AddHeader + * to store a header into the byte buffer of a packet. This method + * should return the number of bytes which are needed to store + * the full header data by Serialize. + * - a method named Deserialize: is used by Packet::RemoveHeader to + * re-create a header from the byte buffer of a packet. The input + * iterator points to the start of the byte buffer from which + * the header should read its data. The data read is expected to + * match bit-for-bit the representation of this header in real + * networks. This method shall return an integer which identifies + * the number of bytes read. + * - a method named Print: is used by Packet::Print to print the + * content of a header as ascii data to a c++ output stream. + * Although the header is free to format its output as it + * wishes, it is recommended to follow a few rules to integrate + * with the packet pretty printer: start with flags, small field + * values located between a pair of parens. Values should be separated + * by whitespace. Follow the parens with the important fields, + * separated by whitespace. + * i.e.: (field1 val1 field2 val2 field3 val3) field4 val4 field5 val5 + * - a method named GetName: is used by Packet::Print to print + * header fragments. This method should return a user-readable + * single word as all capitalized letters. + * + * Sample code which shows how to create a new type of Header, and how to use it, + * is shown in the sample file samples/main-packet-header.cc */ -class Header : public Chunk { -public: - virtual ~Header (); -private: - /** - * \returns a user-readable name to identify this type of header. - * - * The string returned is expected to be a single word with - * all capital letters - */ - virtual std::string DoGetName (void) const = 0; - /** - * \param os the std output stream in which this - * protocol header must print itself. - * - * Although the header is free to format its output as it - * wishes, it is recommended to follow a few rules to integrate - * with the packet pretty printer: - * - start with flags, small field values located between a - * pair of parens. Values should be separated by whitespace. - * - follow the parens with the important fields, separated by - * whitespace. - * i.e.: - * (field1 val1 field2 val2 field3 val3) field4 val4 field5 val5 - */ - virtual void PrintTo (std::ostream &os) const = 0; - - /** - * \returns the size of the serialized Header. - * - * This method is used by Packet::AddHeader to reserve - * enough room in the packet byte buffer prior to calling - * Header::Serialize. - */ - virtual uint32_t GetSerializedSize (void) const = 0; - - /** - * \param start the buffer iterator in which the protocol header - * must serialize itself. This iterator identifies - * the start of the buffer. - */ - virtual void SerializeTo (Buffer::Iterator start) const = 0; - /** - * \param start the buffer iterator from which the protocol header must - * deserialize itself. This iterator identifies - * the start of the buffer. - * \returns the number of bytes read from the buffer - * - * The value returned is used to trim the packet byte buffer of the - * corresponding amount when this method is invoked from - * Packet::RemoveHeader - */ - virtual uint32_t DeserializeFrom (Buffer::Iterator start) = 0; +class Header +{ +protected: + template + static uint32_t AllocateUid (std::string uuid); }; -}; // namespace ns3 +} // namespace ns3 + +namespace ns3 { + +template +uint32_t +Header::AllocateUid (std::string uuid) +{ + return ChunkRegistry::RegisterHeader (uuid); +} + +} // namespace ns3 #endif /* HEADER_H */ diff --git a/src/common/packet-metadata-test.cc b/src/common/packet-metadata-test.cc new file mode 100644 index 000000000..edca62626 --- /dev/null +++ b/src/common/packet-metadata-test.cc @@ -0,0 +1,683 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006,2007 INRIA + * + * 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: Mathieu Lacage + */ +#ifdef RUN_SELF_TESTS + +#include +#include +#include +#include "ns3/test.h" +#include "header.h" +#include "trailer.h" +#include "packet.h" +#include "packet-metadata.h" +#include "packet-printer.h" + +namespace ns3 { + +template +class HistoryHeader : public Header +{ +public: + static uint32_t GetUid (void); + HistoryHeader (); + bool IsOk (void) const; + std::string GetName (void) const; + void Print (std::ostream &os) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator start) const; + uint32_t Deserialize (Buffer::Iterator start); +private: + bool m_ok; +}; + +template +uint32_t +HistoryHeader::GetUid (void) +{ + std::ostringstream oss; + oss << N << "HistoryHeader.ns3"; + static uint32_t uid = AllocateUid > (oss.str()); + return uid; +} + +template +HistoryHeader::HistoryHeader () + : m_ok (false) +{} + +template +bool +HistoryHeader::IsOk (void) const +{ + return m_ok; +} + +template +std::string +HistoryHeader::GetName (void) const +{ + std::ostringstream oss; + oss << N; + return oss.str (); +} + +template +void +HistoryHeader::Print (std::ostream &os) const +{ + NS_ASSERT (false); +} +template +uint32_t +HistoryHeader::GetSerializedSize (void) const +{ + return N; +} +template +void +HistoryHeader::Serialize (Buffer::Iterator start) const +{ + start.WriteU8 (N, N); +} +template +uint32_t +HistoryHeader::Deserialize (Buffer::Iterator start) +{ + m_ok = true; + for (int i = 0; i < N; i++) + { + if (start.ReadU8 () != N) + { + m_ok = false; + } + } + return N; +} + +template +class HistoryTrailer : public Trailer +{ +public: + static uint32_t GetUid (void); + HistoryTrailer (); + bool IsOk (void) const; + std::string GetName (void) const; + void Print (std::ostream &os) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator start) const; + uint32_t Deserialize (Buffer::Iterator start); +private: + bool m_ok; +}; + +template +uint32_t +HistoryTrailer::GetUid (void) +{ + std::ostringstream oss; + oss << N << "HistoryTrailer.ns3"; + static uint32_t uid = AllocateUid > (oss.str ()); + return uid; +} + + +template +HistoryTrailer::HistoryTrailer () + : m_ok (false) +{} + +template +bool +HistoryTrailer::IsOk (void) const +{ + return m_ok; +} + +template +std::string +HistoryTrailer::GetName (void) const +{ + std::ostringstream oss; + oss << N; + return oss.str (); +} +template +void +HistoryTrailer::Print (std::ostream &os) const +{ + NS_ASSERT (false); +} +template +uint32_t +HistoryTrailer::GetSerializedSize (void) const +{ + return N; +} +template +void +HistoryTrailer::Serialize (Buffer::Iterator start) const +{ + start.Prev (N); + start.WriteU8 (N, N); +} +template +uint32_t +HistoryTrailer::Deserialize (Buffer::Iterator start) +{ + m_ok = true; + start.Prev (N); + for (int i = 0; i < N; i++) + { + if (start.ReadU8 () != N) + { + m_ok = false; + } + } + return N; +} + + + +class PacketMetadataTest : public Test { +public: + PacketMetadataTest (); + virtual ~PacketMetadataTest (); + bool CheckHistory (Packet p, const char *file, int line, uint32_t n, ...); + virtual bool RunTests (void); +private: + template + void PrintHeader (std::ostream &os, uint32_t packetUid, uint32_t size, const HistoryHeader *header); + template + void PrintTrailer (std::ostream &os, uint32_t packetUid, uint32_t size, const HistoryTrailer *trailer); + void PrintFragment (std::ostream &os,uint32_t packetUid, + uint32_t size,std::string & name, + struct PacketPrinter::FragmentInformation info); + void PrintPayload (std::ostream &os,uint32_t packetUid, + uint32_t size, + struct PacketPrinter::FragmentInformation info); + template + void RegisterHeader (void); + template + void RegisterTrailer (void); + void CleanupPrints (void); + Packet DoAddHeader (Packet p); + bool Check (const char *file, int line, std::list expected); + + + bool m_headerError; + bool m_trailerError; + std::list m_prints; + PacketPrinter m_printer; +}; + +PacketMetadataTest::PacketMetadataTest () + : Test ("PacketMetadata") +{ + m_printer.SetPayloadPrinter (MakeCallback (&PacketMetadataTest::PrintPayload, this)); + m_printer.SetSeparator (""); +} + +PacketMetadataTest::~PacketMetadataTest () +{} + +template +void +PacketMetadataTest::RegisterHeader (void) +{ + static bool registered = false; + if (!registered) + { + m_printer.SetHeaderPrinter (MakeCallback (&PacketMetadataTest::PrintHeader, this), + MakeCallback (&PacketMetadataTest::PrintFragment, this)); + registered = true; + } +} + +template +void +PacketMetadataTest::RegisterTrailer (void) +{ + static bool registered = false; + if (!registered) + { + m_printer.SetTrailerPrinter (MakeCallback (&PacketMetadataTest::PrintTrailer, this), + MakeCallback (&PacketMetadataTest::PrintFragment, this)); + registered = true; + } +} + + +template +void +PacketMetadataTest::PrintHeader (std::ostream &os, uint32_t packetUid, uint32_t size, + const HistoryHeader *header) +{ + if (!header->IsOk ()) + { + m_headerError = true; + } + m_prints.push_back (N); +} + +template +void +PacketMetadataTest::PrintTrailer (std::ostream &os, uint32_t packetUid, uint32_t size, + const HistoryTrailer *trailer) +{ + if (!trailer->IsOk ()) + { + m_trailerError = true; + } + m_prints.push_back (N); +} +void +PacketMetadataTest::PrintFragment (std::ostream &os,uint32_t packetUid, + uint32_t size,std::string & name, + struct PacketPrinter::FragmentInformation info) +{ + m_prints.push_back (size - (info.end + info.start)); +} +void +PacketMetadataTest::PrintPayload (std::ostream &os,uint32_t packetUid, + uint32_t size, + struct PacketPrinter::FragmentInformation info) +{ + m_prints.push_back (size - (info.end + info.start)); +} + + +void +PacketMetadataTest::CleanupPrints (void) +{ + m_prints.clear (); +} + +bool +PacketMetadataTest::Check (const char *file, int line, std::list expected) +{ + if (m_headerError) + { + Failure () << "PacketMetadata header error. file=" << file + << ", line=" << line << std::endl; + return false; + } + if (m_trailerError) + { + Failure () << "PacketMetadata trailer error. file=" << file + << ", line=" << line << std::endl; + return false; + } + if (expected.size () != m_prints.size ()) + { + goto error; + } + for (std::list::iterator i = m_prints.begin (), + j = expected.begin (); + i != m_prints.end (); i++, j++) + { + NS_ASSERT (j != expected.end ()); + if (*j != *i) + { + goto error; + } + } + return true; + error: + Failure () << "PacketMetadata error. file="<< file + << ", line=" << line << ", got:\""; + for (std::list::iterator i = m_prints.begin (); + i != m_prints.end (); i++) + { + Failure () << *i << ", "; + } + Failure () << "\", expected: \""; + for (std::list::iterator j = expected.begin (); + j != expected.end (); j++) + { + Failure () << *j << ", "; + } + Failure () << "\"" << std::endl; + return false; +} + +bool +PacketMetadataTest::CheckHistory (Packet p, const char *file, int line, uint32_t n, ...) +{ + m_headerError = false; + m_trailerError = false; + std::list expected; + va_list ap; + va_start (ap, n); + for (uint32_t j = 0; j < n; j++) + { + int v = va_arg (ap, int); + expected.push_back (v); + } + va_end (ap); + + m_printer.PrintForward (); + p.Print (Failure (), m_printer); + bool ok = Check (file, line, expected); + CleanupPrints (); + if (!ok) + { + return false; + } + + m_printer.PrintBackward (); + p.Print (Failure (), m_printer); + expected.reverse (); + ok = Check (file, line, expected); + CleanupPrints (); + return ok; +} + +#define ADD_HEADER(p, n) \ + { \ + HistoryHeader header; \ + RegisterHeader (); \ + p.AddHeader (header); \ + } +#define ADD_TRAILER(p, n) \ + { \ + HistoryTrailer trailer; \ + RegisterTrailer (); \ + p.AddTrailer (trailer); \ + } +#define REM_HEADER(p, n) \ + { \ + HistoryHeader header; \ + RegisterHeader (); \ + p.RemoveHeader (header); \ + } +#define REM_TRAILER(p, n) \ + { \ + HistoryTrailer trailer; \ + RegisterTrailer (); \ + p.RemoveTrailer (trailer); \ + } +#define CHECK_HISTORY(p, ...) \ + { \ + if (!CheckHistory (p, __FILE__, \ + __LINE__, __VA_ARGS__)) \ + { \ + ok = false; \ + } \ + Buffer buffer; \ + buffer = p.Serialize (); \ + Packet otherPacket; \ + otherPacket.Deserialize (buffer); \ + if (!CheckHistory (otherPacket, __FILE__, \ + __LINE__, __VA_ARGS__)) \ + { \ + ok = false; \ + } \ + } + + +Packet +PacketMetadataTest::DoAddHeader (Packet p) +{ + ADD_HEADER (p, 10); + return p; +} + +bool +PacketMetadataTest::RunTests (void) +{ + bool ok = true; + + PacketMetadata::Enable (); + + Packet p = Packet (0); + Packet p1 = Packet (0); + + p = Packet (10); + ADD_TRAILER (p, 100); + CHECK_HISTORY (p, 2, 10, 100); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + CHECK_HISTORY (p, 4, + 3, 2, 1, 10); + ADD_HEADER (p, 5); + CHECK_HISTORY (p, 5, + 5, 3, 2, 1, 10); + ADD_HEADER (p, 6); + CHECK_HISTORY (p, 6, + 6, 5, 3, 2, 1, 10); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + REM_HEADER (p, 3); + CHECK_HISTORY (p, 3, + 2, 1, 10); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + REM_HEADER (p, 3); + REM_HEADER (p, 2); + CHECK_HISTORY (p, 2, + 1, 10); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + REM_HEADER (p, 3); + REM_HEADER (p, 2); + REM_HEADER (p, 1); + CHECK_HISTORY (p, 1, 10); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + p1 = p; + REM_HEADER (p1, 3); + REM_HEADER (p1, 2); + REM_HEADER (p1, 1); + CHECK_HISTORY (p1, 1, 10); + CHECK_HISTORY (p, 4, + 3, 2, 1, 10); + ADD_HEADER (p1, 1); + ADD_HEADER (p1, 2); + CHECK_HISTORY (p1, 3, + 2, 1, 10); + CHECK_HISTORY (p, 4, + 3, 2, 1, 10); + ADD_HEADER (p, 3); + CHECK_HISTORY (p, 5, + 3, 3, 2, 1, 10); + ADD_TRAILER (p, 4); + CHECK_HISTORY (p, 6, + 3, 3, 2, 1, 10, 4); + ADD_TRAILER (p, 5); + CHECK_HISTORY (p, 7, + 3, 3, 2, 1, 10, 4, 5); + REM_HEADER (p, 3); + CHECK_HISTORY (p, 6, + 3, 2, 1, 10, 4, 5); + REM_TRAILER (p, 5); + CHECK_HISTORY (p, 5, + 3, 2, 1, 10, 4); + p1 = p; + REM_TRAILER (p, 4); + CHECK_HISTORY (p, 4, + 3, 2, 1, 10); + CHECK_HISTORY (p1, 5, + 3, 2, 1, 10, 4); + p1.RemoveAtStart (3); + CHECK_HISTORY (p1, 4, + 2, 1, 10, 4); + p1.RemoveAtStart (1); + CHECK_HISTORY (p1, 4, + 1, 1, 10, 4); + p1.RemoveAtStart (1); + CHECK_HISTORY (p1, 3, + 1, 10, 4); + p1.RemoveAtEnd (4); + CHECK_HISTORY (p1, 2, + 1, 10); + p1.RemoveAtStart (1); + CHECK_HISTORY (p1, 1, 10); + + p = Packet (10); + ADD_HEADER (p, 8); + ADD_TRAILER (p, 8); + ADD_TRAILER (p, 8); + p.RemoveAtStart (8+10+8); + CHECK_HISTORY (p, 1, 8); + + p = Packet (10); + ADD_HEADER (p, 10); + ADD_HEADER (p, 8); + ADD_TRAILER (p, 6); + ADD_TRAILER (p, 7); + ADD_TRAILER (p, 9); + p.RemoveAtStart (5); + p.RemoveAtEnd (12); + CHECK_HISTORY (p, 5, 3, 10, 10, 6, 4); + + p = Packet (10); + ADD_HEADER (p, 10); + ADD_TRAILER (p, 6); + p.RemoveAtEnd (18); + ADD_TRAILER (p, 5); + ADD_HEADER (p, 3); + CHECK_HISTORY (p, 3, 3, 8, 5); + p.RemoveAtStart (12); + CHECK_HISTORY (p, 1, 4); + p.RemoveAtEnd (2); + CHECK_HISTORY (p, 1, 2); + ADD_HEADER (p, 10); + CHECK_HISTORY (p, 2, 10, 2); + p.RemoveAtEnd (5); + CHECK_HISTORY (p, 1, 7); + + Packet p2 = Packet (0); + Packet p3 = Packet (0); + + p = Packet (40); + ADD_HEADER (p, 5); + ADD_HEADER (p, 8); + CHECK_HISTORY (p, 3, 8, 5, 40); + p1 = p.CreateFragment (0, 5); + p2 = p.CreateFragment (5, 5); + p3 = p.CreateFragment (10, 43); + CHECK_HISTORY (p1, 1, 5); + CHECK_HISTORY (p2, 2, 3, 2); + CHECK_HISTORY (p3, 2, 3, 40); + p1.AddAtEnd (p2); + CHECK_HISTORY (p1, 2, 8, 2); + CHECK_HISTORY (p2, 2, 3, 2); + p1.AddAtEnd (p3); + CHECK_HISTORY (p1, 3, 8, 5, 40); + CHECK_HISTORY (p2, 2, 3, 2); + CHECK_HISTORY (p3, 2, 3, 40); + p1 = p.CreateFragment (0, 5); + CHECK_HISTORY (p1, 1, 5); + + p3 = Packet (50); + ADD_HEADER (p3, 8); + CHECK_HISTORY (p3, 2, 8, 50); + CHECK_HISTORY (p1, 1, 5); + p1.AddAtEnd (p3); + CHECK_HISTORY (p1, 3, 5, 8, 50); + ADD_HEADER (p1, 5); + CHECK_HISTORY (p1, 4, 5, 5, 8, 50); + ADD_TRAILER (p1, 2); + CHECK_HISTORY (p1, 5, 5, 5, 8, 50, 2); + REM_HEADER (p1, 5); + CHECK_HISTORY (p1, 4, 5, 8, 50, 2); + p1.RemoveAtEnd (60); + CHECK_HISTORY (p1, 1, 5); + p1.AddAtEnd (p2); + CHECK_HISTORY (p1, 2, 8, 2); + CHECK_HISTORY (p2, 2, 3, 2); + + p3 = Packet (40); + ADD_HEADER (p3, 5); + ADD_HEADER (p3, 5); + CHECK_HISTORY (p3, 3, 5, 5, 40); + p1 = p3.CreateFragment (0, 5); + p2 = p3.CreateFragment (5, 5); + CHECK_HISTORY (p1, 1, 5); + CHECK_HISTORY (p2, 1, 5); + p1.AddAtEnd (p2); + CHECK_HISTORY (p1, 2, 5, 5); + + p = Packet (0); + CHECK_HISTORY (p, 0); + + p3 = Packet (0); + ADD_HEADER (p3, 5); + ADD_HEADER (p3, 5); + CHECK_HISTORY (p3, 2, 5, 5); + p1 = p3.CreateFragment (0, 4); + p2 = p3.CreateFragment (9, 1); + CHECK_HISTORY (p1, 1, 4); + CHECK_HISTORY (p2, 1, 1); + p1.AddAtEnd (p2); + CHECK_HISTORY (p1, 2, 4, 1); + + + p = Packet (2000); + CHECK_HISTORY (p, 1, 2000); + + p = Packet (); + ADD_TRAILER (p, 10); + ADD_HEADER (p, 5); + p1 = p.CreateFragment (0, 8); + p2 = p.CreateFragment (8, 7); + p1.AddAtEnd (p2); + CHECK_HISTORY (p, 2, 5, 10); + + p = Packet (); + ADD_TRAILER (p, 10); + REM_TRAILER (p, 10); + ADD_TRAILER (p, 10); + CHECK_HISTORY (p, 1, 10); + + p = Packet (); + ADD_HEADER (p, 10); + REM_HEADER (p, 10); + ADD_HEADER (p, 10); + CHECK_HISTORY (p, 1, 10); + + p = Packet (); + ADD_HEADER (p, 10); + p = DoAddHeader (p); + CHECK_HISTORY (p, 2, 10, 10); + + return ok; +} + +static PacketMetadataTest g_packetHistoryTest; + +}//namespace ns3 + +#endif /* RUN_SELF_TESTS */ diff --git a/src/common/packet-metadata.cc b/src/common/packet-metadata.cc index 1308b7d12..4229e15f8 100644 --- a/src/common/packet-metadata.cc +++ b/src/common/packet-metadata.cc @@ -1,7 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2006,2007 INRIA - * All rights reserved. * * 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 @@ -24,8 +23,8 @@ #include "ns3/fatal-error.h" #include "ns3/debug.h" #include "packet-metadata.h" -#include "chunk.h" #include "buffer.h" +#include "chunk-registry.h" NS_DEBUG_COMPONENT_DEFINE ("PacketMetadata"); @@ -965,19 +964,13 @@ PacketMetadata::RemoveAtEnd (uint32_t end) NS_ASSERT (leftToRemove == 0); } -void -PacketMetadata::PrintDefault (std::ostream &os, Buffer buffer) const -{ - Print (os, buffer, PacketPrinter::GetDefault ()); -} - uint32_t PacketMetadata::DoPrint (const struct PacketMetadata::SmallItem *item, const struct PacketMetadata::ExtraItem *extraItem, Buffer data, uint32_t offset, const PacketPrinter &printer, std::ostream &os) const { - uint32_t uid = item->typeUid & 0xfffffffe; + uint32_t uid = (item->typeUid & 0xfffffffe) >> 1; if (uid == 0) { // payload. @@ -992,13 +985,13 @@ PacketMetadata::DoPrint (const struct PacketMetadata::SmallItem *item, extraItem->fragmentStart, item->size - extraItem->fragmentEnd); } - else if (PacketPrinter::IsHeader (uid)) + else if (ChunkRegistry::IsHeader (uid)) { ns3::Buffer::Iterator j = data.Begin (); j.Next (offset); printer.PrintChunk (uid, j, os, extraItem->packetUid, item->size); } - else if (PacketPrinter::IsTrailer (uid)) + else if (ChunkRegistry::IsTrailer (uid)) { ns3::Buffer::Iterator j = data.End (); j.Prev (data.GetSize () - (offset + item->size)); @@ -1094,629 +1087,145 @@ PacketMetadata::Print (std::ostream &os, Buffer data, const PacketPrinter &print } } - - -}; // namespace ns3 - -#include -#include -#include -#include "ns3/test.h" -#include "header.h" -#include "trailer.h" -#include "packet.h" - -namespace ns3 { - -template -class HistoryHeader : public Header -{ -public: - HistoryHeader (); - bool IsOk (void) const; -private: - virtual std::string DoGetName (void) const; - virtual void PrintTo (std::ostream &os) const; - virtual uint32_t GetSerializedSize (void) const; - virtual void SerializeTo (Buffer::Iterator start) const; - virtual uint32_t DeserializeFrom (Buffer::Iterator start); - bool m_ok; -}; - -template -HistoryHeader::HistoryHeader () - : m_ok (false) -{} - -template -bool -HistoryHeader::IsOk (void) const -{ - return m_ok; -} - -template -std::string -HistoryHeader::DoGetName (void) const -{ - std::ostringstream oss; - oss << N; - return oss.str (); -} - -template -void -HistoryHeader::PrintTo (std::ostream &os) const -{ - NS_ASSERT (false); -} -template uint32_t -HistoryHeader::GetSerializedSize (void) const +PacketMetadata::GetSerializedSize (void) const { - return N; -} -template -void -HistoryHeader::SerializeTo (Buffer::Iterator start) const -{ - start.WriteU8 (N, N); -} -template -uint32_t -HistoryHeader::DeserializeFrom (Buffer::Iterator start) -{ - m_ok = true; - for (int i = 0; i < N; i++) + uint32_t totalSize = 0; + totalSize += 4; + if (!m_enable) { - if (start.ReadU8 () != N) - { - m_ok = false; - } + return totalSize; } - return N; + struct PacketMetadata::SmallItem item; + struct PacketMetadata::ExtraItem extraItem; + uint32_t current = m_head; + while (current != 0xffff) + { + ReadItems (current, &item, &extraItem); + uint32_t uid = (item.typeUid & 0xfffffffe) >> 1; + if (uid == 0) + { + totalSize += 4; + } + else + { + totalSize += 4 + ChunkRegistry::GetUidStringFromUid (uid).size (); + } + totalSize += 1 + 4 + 2 + 4 + 4 + 4; + if (current == m_tail) + { + break; + } + NS_ASSERT (current != item.next); + current = item.next; + } + return totalSize; } - -template -class HistoryTrailer : public Trailer -{ -public: - HistoryTrailer (); - bool IsOk (void) const; -private: - virtual std::string DoGetName (void) const; - virtual void PrintTo (std::ostream &os) const; - virtual uint32_t GetSerializedSize (void) const; - virtual void SerializeTo (Buffer::Iterator start) const; - virtual uint32_t DeserializeFrom (Buffer::Iterator start); - bool m_ok; -}; - -template -HistoryTrailer::HistoryTrailer () - : m_ok (false) -{} - -template -bool -HistoryTrailer::IsOk (void) const -{ - return m_ok; -} - -template -std::string -HistoryTrailer::DoGetName (void) const -{ - std::ostringstream oss; - oss << N; - return oss.str (); -} -template void -HistoryTrailer::PrintTo (std::ostream &os) const +PacketMetadata::Serialize (Buffer::Iterator i, uint32_t size) const { - NS_ASSERT (false); + uint32_t bytesWritten = 0; + i.WriteU32 (size); + bytesWritten += 4; + struct PacketMetadata::SmallItem item; + struct PacketMetadata::ExtraItem extraItem; + uint32_t current = m_head; + while (current != 0xffff) + { + ReadItems (current, &item, &extraItem); + NS_DEBUG ("bytesWritten=" << bytesWritten << ", typeUid="< -void -PacketMetadataTest::RegisterHeader (void) -{ - static bool registered = false; - if (!registered) - { - m_printer.AddHeaderPrinter (MakeCallback (&PacketMetadataTest::PrintHeader, this), - MakeCallback (&PacketMetadataTest::PrintFragment, this)); - registered = true; - } -} - -template -void -PacketMetadataTest::RegisterTrailer (void) -{ - static bool registered = false; - if (!registered) - { - m_printer.AddTrailerPrinter (MakeCallback (&PacketMetadataTest::PrintTrailer, this), - MakeCallback (&PacketMetadataTest::PrintFragment, this)); - registered = true; - } -} - - -template -void -PacketMetadataTest::PrintHeader (std::ostream &os, uint32_t packetUid, uint32_t size, - const HistoryHeader *header) -{ - if (!header->IsOk ()) - { - m_headerError = true; - } - m_prints.push_back (N); -} - -template -void -PacketMetadataTest::PrintTrailer (std::ostream &os, uint32_t packetUid, uint32_t size, - const HistoryTrailer *trailer) -{ - if (!trailer->IsOk ()) - { - m_trailerError = true; - } - m_prints.push_back (N); -} -void -PacketMetadataTest::PrintFragment (std::ostream &os,uint32_t packetUid, - uint32_t size,std::string & name, - struct PacketPrinter::FragmentInformation info) -{ - m_prints.push_back (size - (info.end + info.start)); -} -void -PacketMetadataTest::PrintDefault (std::ostream& os,uint32_t packetUid, - uint32_t size,std::string& name, - struct PacketPrinter::FragmentInformation info) -{ - NS_ASSERT (false); -} -void -PacketMetadataTest::PrintPayload (std::ostream &os,uint32_t packetUid, - uint32_t size, - struct PacketPrinter::FragmentInformation info) -{ - m_prints.push_back (size - (info.end + info.start)); -} - - -void -PacketMetadataTest::CleanupPrints (void) -{ - m_prints.clear (); -} - -bool -PacketMetadataTest::Check (const char *file, int line, std::list expected) -{ - if (m_headerError) - { - std::cout << "PacketMetadata header error. file=" << file - << ", line=" << line << std::endl; - return false; - } - if (m_trailerError) - { - std::cout << "PacketMetadata trailer error. file=" << file - << ", line=" << line << std::endl; - return false; - } - if (expected.size () != m_prints.size ()) - { - goto error; - } - for (std::list::iterator i = m_prints.begin (), - j = expected.begin (); - i != m_prints.end (); i++, j++) - { - NS_ASSERT (j != expected.end ()); - if (*j != *i) + else { - goto error; + std::string uidString; + for (uint32_t j = 0; j < uidStringSize; j++) + { + uidString.push_back (i.ReadU8 ()); + size --; + } + uid = ChunkRegistry::GetUidFromUidString (uidString); } + uint8_t isBig = i.ReadU8 (); + size --; + item.typeUid = (uid << 1) | isBig; + item.size = i.ReadU32 (); + size -= 4; + item.chunkUid = i.ReadU16 (); + size -= 2; + extraItem.fragmentStart = i.ReadU32 (); + size -= 4; + extraItem.fragmentEnd = i.ReadU32 (); + size -= 4; + extraItem.packetUid = i.ReadU32 (); + size -= 4; + NS_DEBUG ("size=" << size << ", typeUid="<::iterator i = m_prints.begin (); - i != m_prints.end (); i++) - { - std::cout << *i << ", "; - } - std::cout << "\", expected: \""; - for (std::list::iterator j = expected.begin (); - j != expected.end (); j++) - { - std::cout << *j << ", "; - } - std::cout << "\"" << std::endl; - return false; + NS_ASSERT (size == 0); + return totalSize; } -bool -PacketMetadataTest::CheckHistory (Packet p, const char *file, int line, uint32_t n, ...) -{ - m_headerError = false; - m_trailerError = false; - std::list expected; - va_list ap; - va_start (ap, n); - for (uint32_t j = 0; j < n; j++) - { - int v = va_arg (ap, int); - expected.push_back (v); - } - va_end (ap); - m_printer.PrintForward (); - p.Print (std::cerr, m_printer); - bool ok = Check (file, line, expected); - CleanupPrints (); - if (!ok) - { - return false; - } +} // namespace ns3 - m_printer.PrintBackward (); - p.Print (std::cerr, m_printer); - expected.reverse (); - ok = Check (file, line, expected); - CleanupPrints (); - return ok; -} - -#define ADD_HEADER(p, n) \ - { \ - HistoryHeader header; \ - RegisterHeader (); \ - p.AddHeader (header); \ - } -#define ADD_TRAILER(p, n) \ - { \ - HistoryTrailer trailer; \ - RegisterTrailer (); \ - p.AddTrailer (trailer); \ - } -#define REM_HEADER(p, n) \ - { \ - HistoryHeader header; \ - RegisterHeader (); \ - p.RemoveHeader (header); \ - } -#define REM_TRAILER(p, n) \ - { \ - HistoryTrailer trailer; \ - RegisterTrailer (); \ - p.RemoveTrailer (trailer); \ - } -#define CHECK_HISTORY(p, ...) \ - { \ - if (!CheckHistory (p, __FILE__, \ - __LINE__, __VA_ARGS__)) \ - { \ - ok = false; \ - } \ - } - -bool -PacketMetadataTest::RunTests (void) -{ - bool ok = true; - - PacketMetadata::Enable (); - - Packet p = Packet (0); - Packet p1 = Packet (0); - - p = Packet (10); - ADD_TRAILER (p, 100); - CHECK_HISTORY (p, 2, 10, 100); - - p = Packet (10); - ADD_HEADER (p, 1); - ADD_HEADER (p, 2); - ADD_HEADER (p, 3); - CHECK_HISTORY (p, 4, - 3, 2, 1, 10); - ADD_HEADER (p, 5); - CHECK_HISTORY (p, 5, - 5, 3, 2, 1, 10); - ADD_HEADER (p, 6); - CHECK_HISTORY (p, 6, - 6, 5, 3, 2, 1, 10); - - p = Packet (10); - ADD_HEADER (p, 1); - ADD_HEADER (p, 2); - ADD_HEADER (p, 3); - REM_HEADER (p, 3); - CHECK_HISTORY (p, 3, - 2, 1, 10); - - p = Packet (10); - ADD_HEADER (p, 1); - ADD_HEADER (p, 2); - ADD_HEADER (p, 3); - REM_HEADER (p, 3); - REM_HEADER (p, 2); - CHECK_HISTORY (p, 2, - 1, 10); - - p = Packet (10); - ADD_HEADER (p, 1); - ADD_HEADER (p, 2); - ADD_HEADER (p, 3); - REM_HEADER (p, 3); - REM_HEADER (p, 2); - REM_HEADER (p, 1); - CHECK_HISTORY (p, 1, 10); - - p = Packet (10); - ADD_HEADER (p, 1); - ADD_HEADER (p, 2); - ADD_HEADER (p, 3); - p1 = p; - REM_HEADER (p1, 3); - REM_HEADER (p1, 2); - REM_HEADER (p1, 1); - CHECK_HISTORY (p1, 1, 10); - CHECK_HISTORY (p, 4, - 3, 2, 1, 10); - ADD_HEADER (p1, 1); - ADD_HEADER (p1, 2); - CHECK_HISTORY (p1, 3, - 2, 1, 10); - CHECK_HISTORY (p, 4, - 3, 2, 1, 10); - ADD_HEADER (p, 3); - CHECK_HISTORY (p, 5, - 3, 3, 2, 1, 10); - ADD_TRAILER (p, 4); - CHECK_HISTORY (p, 6, - 3, 3, 2, 1, 10, 4); - ADD_TRAILER (p, 5); - CHECK_HISTORY (p, 7, - 3, 3, 2, 1, 10, 4, 5); - REM_HEADER (p, 3); - CHECK_HISTORY (p, 6, - 3, 2, 1, 10, 4, 5); - REM_TRAILER (p, 5); - CHECK_HISTORY (p, 5, - 3, 2, 1, 10, 4); - p1 = p; - REM_TRAILER (p, 4); - CHECK_HISTORY (p, 4, - 3, 2, 1, 10); - CHECK_HISTORY (p1, 5, - 3, 2, 1, 10, 4); - p1.RemoveAtStart (3); - CHECK_HISTORY (p1, 4, - 2, 1, 10, 4); - p1.RemoveAtStart (1); - CHECK_HISTORY (p1, 4, - 1, 1, 10, 4); - p1.RemoveAtStart (1); - CHECK_HISTORY (p1, 3, - 1, 10, 4); - p1.RemoveAtEnd (4); - CHECK_HISTORY (p1, 2, - 1, 10); - p1.RemoveAtStart (1); - CHECK_HISTORY (p1, 1, 10); - - p = Packet (10); - ADD_HEADER (p, 8); - ADD_TRAILER (p, 8); - ADD_TRAILER (p, 8); - p.RemoveAtStart (8+10+8); - CHECK_HISTORY (p, 1, 8); - - p = Packet (10); - ADD_HEADER (p, 10); - ADD_HEADER (p, 8); - ADD_TRAILER (p, 6); - ADD_TRAILER (p, 7); - ADD_TRAILER (p, 9); - p.RemoveAtStart (5); - p.RemoveAtEnd (12); - CHECK_HISTORY (p, 5, 3, 10, 10, 6, 4); - - p = Packet (10); - ADD_HEADER (p, 10); - ADD_TRAILER (p, 6); - p.RemoveAtEnd (18); - ADD_TRAILER (p, 5); - ADD_HEADER (p, 3); - CHECK_HISTORY (p, 3, 3, 8, 5); - p.RemoveAtStart (12); - CHECK_HISTORY (p, 1, 4); - p.RemoveAtEnd (2); - CHECK_HISTORY (p, 1, 2); - ADD_HEADER (p, 10); - CHECK_HISTORY (p, 2, 10, 2); - p.RemoveAtEnd (5); - CHECK_HISTORY (p, 1, 7); - - Packet p2 = Packet (0); - Packet p3 = Packet (0); - - p = Packet (40); - ADD_HEADER (p, 5); - ADD_HEADER (p, 8); - CHECK_HISTORY (p, 3, 8, 5, 40); - p1 = p.CreateFragment (0, 5); - p2 = p.CreateFragment (5, 5); - p3 = p.CreateFragment (10, 43); - CHECK_HISTORY (p1, 1, 5); - CHECK_HISTORY (p2, 2, 3, 2); - CHECK_HISTORY (p3, 2, 3, 40); - p1.AddAtEnd (p2); - CHECK_HISTORY (p1, 2, 8, 2); - CHECK_HISTORY (p2, 2, 3, 2); - p1.AddAtEnd (p3); - CHECK_HISTORY (p1, 3, 8, 5, 40); - CHECK_HISTORY (p2, 2, 3, 2); - CHECK_HISTORY (p3, 2, 3, 40); - p1 = p.CreateFragment (0, 5); - CHECK_HISTORY (p1, 1, 5); - - p3 = Packet (50); - ADD_HEADER (p3, 8); - CHECK_HISTORY (p3, 2, 8, 50); - CHECK_HISTORY (p1, 1, 5); - p1.AddAtEnd (p3); - CHECK_HISTORY (p1, 3, 5, 8, 50); - ADD_HEADER (p1, 5); - CHECK_HISTORY (p1, 4, 5, 5, 8, 50); - ADD_TRAILER (p1, 2); - CHECK_HISTORY (p1, 5, 5, 5, 8, 50, 2); - REM_HEADER (p1, 5); - CHECK_HISTORY (p1, 4, 5, 8, 50, 2); - p1.RemoveAtEnd (60); - CHECK_HISTORY (p1, 1, 5); - p1.AddAtEnd (p2); - CHECK_HISTORY (p1, 2, 8, 2); - CHECK_HISTORY (p2, 2, 3, 2); - - p3 = Packet (40); - ADD_HEADER (p3, 5); - ADD_HEADER (p3, 5); - CHECK_HISTORY (p3, 3, 5, 5, 40); - p1 = p3.CreateFragment (0, 5); - p2 = p3.CreateFragment (5, 5); - CHECK_HISTORY (p1, 1, 5); - CHECK_HISTORY (p2, 1, 5); - p1.AddAtEnd (p2); - CHECK_HISTORY (p1, 2, 5, 5); - - p = Packet (0); - CHECK_HISTORY (p, 0); - - p3 = Packet (0); - ADD_HEADER (p3, 5); - ADD_HEADER (p3, 5); - CHECK_HISTORY (p3, 2, 5, 5); - p1 = p3.CreateFragment (0, 4); - p2 = p3.CreateFragment (9, 1); - CHECK_HISTORY (p1, 1, 4); - CHECK_HISTORY (p2, 1, 1); - p1.AddAtEnd (p2); - CHECK_HISTORY (p1, 2, 4, 1); - - - p = Packet (2000); - CHECK_HISTORY (p, 1, 2000); - - p = Packet (); - ADD_TRAILER (p, 10); - ADD_HEADER (p, 5); - p1 = p.CreateFragment (0, 8); - p2 = p.CreateFragment (8, 7); - p1.AddAtEnd (p2); - CHECK_HISTORY (p, 2, 5, 10); - - p = Packet (); - ADD_TRAILER (p, 10); - REM_TRAILER (p, 10); - ADD_TRAILER (p, 10); - CHECK_HISTORY (p, 1, 10); - - p = Packet (); - ADD_HEADER (p, 10); - REM_HEADER (p, 10); - ADD_HEADER (p, 10); - CHECK_HISTORY (p, 1, 10); - - return ok; -} - -static PacketMetadataTest g_packetHistoryTest; - -}//namespace ns3 diff --git a/src/common/packet-metadata.h b/src/common/packet-metadata.h index be102a4d3..12223a4bb 100644 --- a/src/common/packet-metadata.h +++ b/src/common/packet-metadata.h @@ -100,9 +100,12 @@ public: uint32_t GetUid (void) const; - void PrintDefault (std::ostream &os, Buffer buffer) const; void Print (std::ostream &os, Buffer buffer, PacketPrinter const &printer) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator i, uint32_t size) const; + uint32_t Deserialize (Buffer::Iterator i); + static void PrintStats (void); private: @@ -254,26 +257,26 @@ template void PacketMetadata::AddHeader (T const &header, uint32_t size) { - DoAddHeader (PacketPrinter::GetHeaderUid (), size); + DoAddHeader (T::GetUid () << 1, size); } template void PacketMetadata::RemoveHeader (T const &header, uint32_t size) { - DoRemoveHeader (PacketPrinter::GetHeaderUid (), size); + DoRemoveHeader (T::GetUid () << 1, size); } template void PacketMetadata::AddTrailer (T const &trailer, uint32_t size) { - DoAddTrailer (PacketPrinter::GetTrailerUid (), size); + DoAddTrailer (T::GetUid () << 1, size); } template void PacketMetadata::RemoveTrailer (T const &trailer, uint32_t size) { - DoRemoveTrailer (PacketPrinter::GetTrailerUid (), size); + DoRemoveTrailer (T::GetUid () << 1, size); } @@ -303,24 +306,23 @@ PacketMetadata::PacketMetadata (PacketMetadata const &o) PacketMetadata & PacketMetadata::operator = (PacketMetadata const& o) { - if (m_data == o.m_data) + if (m_data != o.m_data) { - // self assignment - return *this; + // not self assignment + NS_ASSERT (m_data != 0); + m_data->m_count--; + if (m_data->m_count == 0) + { + PacketMetadata::Recycle (m_data); + } + m_data = o.m_data; + NS_ASSERT (m_data != 0); + m_data->m_count++; } - NS_ASSERT (m_data != 0); - m_data->m_count--; - if (m_data->m_count == 0) - { - PacketMetadata::Recycle (m_data); - } - m_data = o.m_data; m_head = o.m_head; m_tail = o.m_tail; m_used = o.m_used; m_packetUid = o.m_packetUid; - NS_ASSERT (m_data != 0); - m_data->m_count++; return *this; } PacketMetadata::~PacketMetadata () diff --git a/src/common/packet-printer.cc b/src/common/packet-printer.cc index 9ccda7668..bfd0fba01 100644 --- a/src/common/packet-printer.cc +++ b/src/common/packet-printer.cc @@ -20,12 +20,13 @@ */ #include "packet-printer.h" +#include "chunk-registry.h" namespace ns3 { PacketPrinter::PacketPrinter () : m_forward (true), - m_separator ("") + m_separator (" ") {} void @@ -44,44 +45,10 @@ PacketPrinter::SetSeparator (std::string separator) m_separator = separator; } void -PacketPrinter::AddPayloadPrinter (PayloadPrinter printer) +PacketPrinter::SetPayloadPrinter (PayloadPrinter printer) { m_payloadPrinter = printer; } -void -PacketPrinter::AddDefaultPrinter (DefaultPrinter printer) -{ - m_defaultPrinter = printer; -} - -PacketPrinter::RegisteredChunks * -PacketPrinter::GetRegisteredChunks (void) -{ - static RegisteredChunks registeredChunks; - return ®isteredChunks; -} - -PacketPrinter -PacketPrinter::GetDefault (void) -{ - return *(PacketPrinter::PeekDefault ()); -} -PacketPrinter * -PacketPrinter::PeekDefault (void) -{ - static PacketPrinter *tmp = PacketPrinter::CreateStaticDefault (); - return tmp; -} -PacketPrinter * -PacketPrinter::CreateStaticDefault (void) -{ - static PacketPrinter tmp; - tmp.PrintForward (); - tmp.AddPayloadPrinter (MakeCallback (&PacketPrinter::DoDefaultPrintPayload)); - tmp.SetSeparator (" "); - return &tmp; -} - void PacketPrinter::PrintChunk (uint32_t chunkUid, @@ -90,26 +57,22 @@ PacketPrinter::PrintChunk (uint32_t chunkUid, uint32_t packetUid, uint32_t size) const { - RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks (); - NS_ASSERT (chunkUid >= 1 && chunkUid/2 <= registeredChunks->size ()); + uint8_t *instance = ChunkRegistry::GetStaticInstance (chunkUid); + ChunkRegistry::Deserialize (chunkUid, instance, start); + for (PrinterList::const_iterator i = m_printerList.begin (); i != m_printerList.end (); i++) { if (i->m_chunkUid == chunkUid) { - DoPrintCallback cb = (*registeredChunks)[chunkUid/2-1].printCallback; - cb (i->m_printer, start, os, packetUid, size); + ChunkRegistry::InvokePrintCallback (chunkUid, instance, os, packetUid, size, i->m_printer); return; } } - DoGetNameCallback cb = (*registeredChunks)[chunkUid/2-1].getNameCallback; - std::string name = cb (); - struct PacketPrinter::FragmentInformation info; - info.start = 0; - info.end = 0; - if (!m_defaultPrinter.IsNull ()) - { - m_defaultPrinter (os, packetUid, size, name, info); - } + // if the over did not override this type of chunk, + // we print something by default. + std::string name = ChunkRegistry::GetName (chunkUid, instance); + os << name; + ChunkRegistry::Print (chunkUid, instance, os); } void PacketPrinter::PrintChunkFragment (uint32_t chunkUid, @@ -119,10 +82,8 @@ PacketPrinter::PrintChunkFragment (uint32_t chunkUid, uint32_t fragmentStart, uint32_t fragmentEnd) const { - RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks (); - NS_ASSERT (chunkUid >= 1 && chunkUid/2 <= registeredChunks->size ()); - DoGetNameCallback cb = (*registeredChunks)[chunkUid/2-1].getNameCallback; - std::string name = cb (); + uint8_t *instance = ChunkRegistry::GetStaticInstance (chunkUid); + std::string name = ChunkRegistry::GetName (chunkUid, instance); struct PacketPrinter::FragmentInformation info; info.start = fragmentStart; info.end = fragmentEnd; @@ -130,14 +91,20 @@ PacketPrinter::PrintChunkFragment (uint32_t chunkUid, { if (i->m_chunkUid == chunkUid) { + i->m_fragmentPrinter (os, packetUid, size, name, info); return; } } - if (!m_defaultPrinter.IsNull ()) - { - m_defaultPrinter (os, packetUid, size, name, info); - } + // if the user did not override this type of chunk, + // we print something by default. + NS_ASSERT (info.start != 0 || info.end != 0); + os << name << " " + << "(" + << "length " << size - (info.end + info.start) << " " + << "trim_start " << info.start << " " + << "trim_end " << info.end + << ")"; } void PacketPrinter::PrintPayload (std::ostream &os, uint32_t packetUid, uint32_t size, @@ -149,15 +116,10 @@ PacketPrinter::PrintPayload (std::ostream &os, uint32_t packetUid, uint32_t size if (!m_payloadPrinter.IsNull ()) { m_payloadPrinter (os, packetUid, size, info); + return; } -} - -void -PacketPrinter::DoDefaultPrintPayload (std::ostream & os, - uint32_t packetUid, - uint32_t size, - struct PacketPrinter::FragmentInformation info) -{ + // if the user did not override the payload printer, + // we print something anyway. os << "DATA (" << "length " << size - (info.end + info.start); if (info.start != 0 || info.end != 0) @@ -167,69 +129,7 @@ PacketPrinter::DoDefaultPrintPayload (std::ostream & os, << "trim_end " << info.end; } os << ")"; -} -void -PacketPrinter::DoDefaultPrintDefault (std::ostream & os, - uint32_t packetUid, - uint32_t size, - std::string &name, - struct PacketPrinter::FragmentInformation info) -{ - NS_ASSERT_MSG (false, "This should never happen because we provide a printer for _all_ chunk types."); } -void -PacketPrinter::DoDefaultPrintFragment (std::ostream & os, - uint32_t packetUid, - uint32_t size, - std::string &name, - struct PacketPrinter::FragmentInformation info) -{ - NS_ASSERT (info.start != 0 || info.end != 0); - os << name << " " - << "(" - << "length " << size - (info.end + info.start) << " " - << "trim_start " << info.start << " " - << "trim_end " << info.end - << ")" - ; -} - -void -PacketPrinter::DoAddPrinter (uint32_t uid, - Ptr printer, - Callback fragmentPrinter) -{ - struct PacketPrinter::Printer p; - p.m_chunkUid = uid; - p.m_printer = printer; - p.m_fragmentPrinter = fragmentPrinter; - m_printerList.push_back (p); -} - -bool -PacketPrinter::IsTrailer (uint32_t uid) -{ - RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks (); - NS_ASSERT (uid >= 1 && uid/2 <= registeredChunks->size ()); - bool isHeader = (*registeredChunks)[uid/2-1].isHeader; - return !isHeader; -} -bool -PacketPrinter::IsHeader (uint32_t uid) -{ - RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks (); - NS_ASSERT (uid >= 1 && uid/2 <= registeredChunks->size ()); - bool isHeader = (*registeredChunks)[uid/2-1].isHeader; - return isHeader; -} - - - } // namespace ns3 diff --git a/src/common/packet-printer.h b/src/common/packet-printer.h index cd340545d..22aede84c 100644 --- a/src/common/packet-printer.h +++ b/src/common/packet-printer.h @@ -26,14 +26,8 @@ #include "buffer.h" #include -namespace { - class ItemList; -} - namespace ns3 { -class Chunk; - /** * \brief hold a list of print callbacks for packet headers and trailers * @@ -92,99 +86,38 @@ public: * Print the content of the packet backward. */ void PrintBackward (void); + /** + * \param separator the new separator + * + * The default separator is a single space character. + */ void SetSeparator (std::string separator); /** * \param printer printer for payload */ - void AddPayloadPrinter (PayloadPrinter printer); + void SetPayloadPrinter (PayloadPrinter printer); /** * \param printer printer for the specified chunk * \param fragmentPrinter printer for a fragment of the specified chunk * - * If the user has not specified a callback for a specific header present - * in a packet, the "default" callback is invoked. If no such callback - * was specified, nothing happens. + * If the user does not force a user-specific printing function through + * a call to SetHeaderPrinter, the default print output is generated. */ template - void AddHeaderPrinter (Callback printer, + void SetHeaderPrinter (Callback printer, ChunkFragmentPrinter fragmentPrinter); /** * \param printer printer for the specified chunk * \param fragmentPrinter printer for a fragment of the specified chunk * - * If the user has not specified a callback for a specific trailer present - * in a packet, the "default" callback is invoked. If no such callback - * was specified, nothing happens. + * If the user does not force a user-specific printing function through + * a call to SetTrailerPrinter, the default print output is generated. */ template - void AddTrailerPrinter (Callback printer, - ChunkFragmentPrinter fragmentPrinter); - /** - * \param printer printer for a chunk for which no callback was specified explicitely - */ - void AddDefaultPrinter (DefaultPrinter printer); - + void SetTrailerPrinter (Callback printer, + ChunkFragmentPrinter fragmentPrinter); private: friend class PacketMetadata; - typedef void (*DoPrintCallback) (Ptr, Buffer::Iterator, std::ostream &, - uint32_t, uint32_t); - typedef std::string (*DoGetNameCallback) (void); - struct Printer - { - uint32_t m_chunkUid; - Ptr m_printer; - Callback - m_fragmentPrinter; - }; - struct RegisteredChunk - { - DoPrintCallback printCallback; - DoGetNameCallback getNameCallback; - bool isHeader; - }; - typedef std::vector PrinterList; - typedef std::vector RegisteredChunks; - - - static PacketPrinter GetDefault (void); - static PacketPrinter *PeekDefault (void); - static PacketPrinter *CreateStaticDefault (void); - static void DoDefaultPrintPayload (std::ostream & os, - uint32_t packetUid, - uint32_t size, - struct PacketPrinter::FragmentInformation info); - static void DoDefaultPrintDefault (std::ostream & os, - uint32_t packetUid, - uint32_t size, - std::string &name, - struct PacketPrinter::FragmentInformation info); - template - static void DoDefaultPrint (std::ostream &os, uint32_t packetUid, uint32_t size, const T *chunk); - static void DoDefaultPrintFragment (std::ostream & os, - uint32_t packetUid, - uint32_t size, - std::string &name, - struct PacketPrinter::FragmentInformation info); - - template - static void DoPrint (Ptr callbackPrinter, - Buffer::Iterator i, - std::ostream &os, - uint32_t packetUid, - uint32_t size); - template - static std::string DoGetName (void); - template - static uint32_t GetTrailerUid (void); - template - static uint32_t GetHeaderUid (void); - template - static uint32_t AllocateUid (bool isHeader); - static RegisteredChunks *GetRegisteredChunks (void); - static bool IsTrailer (uint32_t uid); - static bool IsHeader (uint32_t uid); - - void PrintChunk (uint32_t uid, Buffer::Iterator i, std::ostream &os, @@ -198,16 +131,15 @@ private: uint32_t fragmentEnd) const; void PrintPayload (std::ostream &os, uint32_t packetUid, uint32_t size, uint32_t fragmentStart, uint32_t fragmentEnd) const; - void DoAddPrinter (uint32_t uid, - Ptr printer, - Callback fragmentPrinter); - - static PacketPrinter m_defaultPacketPrinter; + struct Printer + { + uint32_t m_chunkUid; + Ptr m_printer; + Callback m_fragmentPrinter; + }; + typedef std::vector PrinterList; + PrinterList m_printerList; PayloadPrinter m_payloadPrinter; DefaultPrinter m_defaultPrinter; @@ -221,97 +153,28 @@ namespace ns3 { template void -PacketPrinter::AddHeaderPrinter (Callback printer, - Callback fragmentPrinter) +PacketPrinter::SetHeaderPrinter (Callback printer, + ChunkFragmentPrinter fragmentPrinter) { - static uint32_t uid = PacketPrinter::GetHeaderUid (); - DoAddPrinter (uid, printer.GetImpl (), fragmentPrinter); + Printer p; + p.m_chunkUid = T::GetUid (); + p.m_printer = printer.GetImpl (); + p.m_fragmentPrinter = fragmentPrinter; + m_printerList.push_back (p); } template void -PacketPrinter::AddTrailerPrinter (Callback printer, - Callback fragmentPrinter) +PacketPrinter::SetTrailerPrinter (Callback printer, + ChunkFragmentPrinter fragmentPrinter) { - static uint32_t uid = PacketPrinter::GetTrailerUid (); - DoAddPrinter (uid, printer.GetImpl (), fragmentPrinter); + Printer p; + p.m_chunkUid = T::GetUid (); + p.m_printer = printer.GetImpl (); + p.m_fragmentPrinter = fragmentPrinter; + m_printerList.push_back (p); } -template -void -PacketPrinter::DoPrint (Ptr printerCallback, - Buffer::Iterator i, - std::ostream &os, - uint32_t packetUid, - uint32_t size) -{ - T chunk = T (); - chunk.Deserialize (i); - Callback callback; - callback.Assign (printerCallback); - callback (os, packetUid, size, &chunk); -} - -template -std::string -PacketPrinter::DoGetName (void) -{ - T chunk = T (); - return chunk.GetName (); -} - -template -uint32_t -PacketPrinter::GetHeaderUid (void) -{ - static uint32_t uid = PacketPrinter::AllocateUid (true); - return uid; -} - -template -uint32_t -PacketPrinter::GetTrailerUid (void) -{ - static uint32_t uid = PacketPrinter::AllocateUid (false); - return uid; -} - -template -uint32_t -PacketPrinter::AllocateUid (bool isHeader) -{ - RegisteredChunks *chunks = PacketPrinter::GetRegisteredChunks (); - RegisteredChunk chunk; - chunk.printCallback = &PacketPrinter::DoPrint; - chunk.getNameCallback = &PacketPrinter::DoGetName; - chunk.isHeader = isHeader; - chunks->push_back (chunk); - uint32_t uid = chunks->size () * 2; - PacketPrinter::PeekDefault ()->DoAddPrinter (uid, - MakeCallback (&PacketPrinter::DoDefaultPrint).GetImpl (), - MakeCallback (&PacketPrinter::DoDefaultPrintFragment)); - return uid; -} - -template -void -PacketPrinter::DoDefaultPrint (std::ostream &os, uint32_t packetUid, uint32_t size, const T *chunk) -{ - os << chunk->GetName () << " "; - chunk->Print (os); -} - - } // namespace ns3 diff --git a/src/common/packet.cc b/src/common/packet.cc index 8f989f8f3..5159f3e2c 100644 --- a/src/common/packet.cc +++ b/src/common/packet.cc @@ -74,14 +74,14 @@ Packet::GetSize (void) const void Packet::AddAtEnd (Packet packet) { - packet.m_buffer.TransformIntoRealBuffer (); - m_buffer.TransformIntoRealBuffer (); + Buffer src = packet.m_buffer.CreateFullCopy (); + Buffer dst = m_buffer.CreateFullCopy (); - Buffer src = packet.m_buffer; - m_buffer.AddAtEnd (src.GetSize ()); - Buffer::Iterator destStart = m_buffer.End (); + dst.AddAtEnd (src.GetSize ()); + Buffer::Iterator destStart = dst.End (); destStart.Prev (src.GetSize ()); destStart.Write (src.Begin (), src.End ()); + m_buffer = dst; /** * XXX: we might need to merge the tag list of the * other packet into the current packet. @@ -125,10 +125,16 @@ Packet::GetUid (void) const return m_metadata.GetUid (); } +void +Packet::PrintTags (std::ostream &os) const +{ + m_tags.Print (os, " "); +} + void Packet::Print (std::ostream &os) const { - m_metadata.PrintDefault (os, m_buffer); + m_metadata.Print (os, m_buffer, PacketPrinter ()); } void @@ -143,7 +149,58 @@ Packet::EnableMetadata (void) PacketMetadata::Enable (); } -}; // namespace ns3 +Buffer +Packet::Serialize (void) const +{ + Buffer buffer; + uint32_t reserve; + + // write metadata + reserve = m_metadata.GetSerializedSize (); + buffer.AddAtStart (reserve); + m_metadata.Serialize (buffer.Begin (), reserve); + + // write tags + reserve = m_tags.GetSerializedSize (); + buffer.AddAtStart (reserve); + m_tags.Serialize (buffer.Begin (), reserve); + + // aggregate byte buffer, metadata, and tags + Buffer tmp = m_buffer.CreateFullCopy (); + buffer.AddAtStart (tmp.GetSize ()); + buffer.Begin ().Write (tmp.Begin (), tmp.End ()); + + // write byte buffer size. + buffer.AddAtStart (4); + buffer.Begin ().WriteU32 (m_buffer.GetSize ()); + + return buffer; +} +void +Packet::Deserialize (Buffer buffer) +{ + Buffer buf = buffer; + // read size + uint32_t packetSize = buf.Begin ().ReadU32 (); + buf.RemoveAtStart (4); + + // read buffer. + buf.RemoveAtEnd (buf.GetSize () - packetSize); + m_buffer = buf; + + // read tags + buffer.RemoveAtStart (4 + packetSize); + uint32_t tagsDeserialized = m_tags.Deserialize (buffer.Begin ()); + buffer.RemoveAtStart (tagsDeserialized); + + // read metadata + uint32_t metadataDeserialized = + m_metadata.Deserialize (buffer.Begin ()); + buffer.RemoveAtStart (metadataDeserialized); +} + + +} // namespace ns3 diff --git a/src/common/packet.h b/src/common/packet.h index 09090164c..00c540600 100644 --- a/src/common/packet.h +++ b/src/common/packet.h @@ -27,6 +27,7 @@ #include "trailer.h" #include "tags.h" #include "packet-metadata.h" +#include "tag.h" #include "ns3/callback.h" #include "ns3/assert.h" @@ -37,12 +38,15 @@ class PacketPrinter; /** * \brief network packets * - * Each network packet contains a byte buffer and a list of tags. + * Each network packet contains a byte buffer, a list of tags, and + * metadata. + * * - The byte buffer stores the serialized content of the headers and trailers * added to a packet. The serialized representation of these headers is expected * to match that of real network packets bit for bit (although nothing * forces you to do this) which means that the content of a packet buffer * is expected to be that of a real packet. + * * - The list of tags stores an arbitrarily large set of arbitrary * user-provided data structures in the packet: only one instance of * each type of data structure is allowed in a list of tags. @@ -51,43 +55,22 @@ class PacketPrinter; * 16 bytes big. Trying to attach bigger data structures will trigger * crashes at runtime. * - * Implementing a new type of Header for a new protocol is pretty easy - * and is a matter of creating a subclass of the ns3::Header base class, - * and implementing the 4 pure virtual methods defined in ns3::Header. - * Sample code which shows how to create such a new Header, how to use - * it, and how to manipulate tags is shown below: - * \include samples/main-packet.cc + * - The metadata describes the type of the headers and trailers which + * were serialized in the byte buffer. The maintenance of metadata is + * optional and disabled by default. To enable it, you must call + * Packet::EnableMetadata and this will allow you to get non-empty + * output from Packet::Print and Packet::Print. * - * The current implementation of the byte buffers and tag list is based - * on COW (Copy On Write. An introduction to COW can be found in Scott - * Meyer's "More Effective C++", items 17 and 29). What this means is that - * copying packets without modifying them is very cheap (in terms of cpu - * and memory usage) and modifying them can be also very cheap. What is - * key for proper COW implementations is being - * able to detect when a given modification of the state of a packet triggers - * a full copy of the data prior to the modification: COW systems need - * to detect when an operation is "dirty". + * Implementing a new type of Header or Trailer for a new protocol is + * pretty easy and is a matter of creating a subclass of the ns3::Header + * or of the ns3::Trailer base class, and implementing the methods + * described in their respective API documentation. * - * Dirty operations: - * - ns3::Packet::RemoveTag - * - ns3::Packet::Add - * - both versions of ns3::Packet::AddAtEnd + * Implementing a new type of Tag requires roughly the same amount of + * work and this work is described in the ns3::Tag API documentation. * - * Non-dirty operations: - * - ns3::Packet::AddTag - * - ns3::Packet::RemoveAllTags - * - ns3::Packet::PeekTag - * - ns3::Packet::Peek - * - ns3::Packet::Remove - * - ns3::Packet::CreateFragment - * - ns3::Packet::RemoveAtStart - * - ns3::Packet::RemoveAtEnd - * - * Dirty operations will always be slower than non-dirty operations, - * sometimes by several orders of magnitude. However, even the - * dirty operations have been optimized for common use-cases which - * means that most of the time, these operations will not trigger - * data copies and will thus be still very fast. + * The performance aspects of the Packet API are discussed in + * \ref packetperf */ class Packet { public: @@ -132,7 +115,7 @@ public: uint32_t GetSize (void) const; /** * Add header to this packet. This method invokes the - * ns3::Chunk::GetSerializedSize and ns3::Chunk::SerializeTo + * GetSerializedSize and Serialize * methods to reserve space in the buffer and request the * header to serialize itself in the packet buffer. * @@ -142,7 +125,7 @@ public: void AddHeader (T const &header); /** * Deserialize and remove the header from the internal buffer. - * This method invokes ns3::Chunk::DeserializeFrom. + * This method invokes Deserialize. * * \param header a reference to the header to remove from the internal buffer. * \returns the number of bytes removed from the packet. @@ -151,7 +134,7 @@ public: uint32_t RemoveHeader (T &header); /** * Add trailer to this packet. This method invokes the - * ns3::Chunk::GetSerializedSize and ns3::Trailer::serializeTo + * GetSerializedSize and Serialize * methods to reserve space in the buffer and request the trailer * to serialize itself in the packet buffer. * @@ -161,7 +144,7 @@ public: void AddTrailer (T const &trailer); /** * Remove a deserialized trailer from the internal buffer. - * This method invokes the ns3::Chunk::DeserializeFrom method. + * This method invokes the Deserialize method. * * \param trailer a reference to the trailer to remove from the internal buffer. * \returns the number of bytes removed from the end of the packet. @@ -171,7 +154,8 @@ public: /** * Attach a tag to this packet. The tag is fully copied * in a packet-specific internal buffer. This operation - * is expected to be really fast. + * is expected to be really fast. The copy constructor of the + * tag is invoked to copy it into the tag buffer. * * \param tag a pointer to the tag to attach to this packet. */ @@ -198,6 +182,8 @@ public: /** * Copy a tag stored internally to the input tag. If no instance * of this tag is present internally, the input tag is not modified. + * The copy constructor of the tag is invoked to copy it into the + * input tag variable. * * \param tag a pointer to the tag to read from this packet * \returns true if an instance of this tag type is stored @@ -210,6 +196,14 @@ public: * much much faster than invoking removeTag n times. */ void RemoveAllTags (void); + /** + * \param os output stream in which the data should be printed. + * + * Iterate over the tags present in this packet, and + * invoke the Print method of each tag stored in the packet. + */ + void PrintTags (std::ostream &os) const; + /** * Concatenate the input packet at the end of the current * packet. This does not alter the uid of either packet. @@ -294,6 +288,38 @@ public: * errors will be detected and will abort the program. */ static void EnableMetadata (void); + + /** + * \returns a byte buffer + * + * This method creates a serialized representation of a Packet object + * ready to be transmitted over a network to another system. This + * serialized representation contains a copy of the packet byte buffer, + * the tag list, and the packet metadata (if there is one). + * + * This method will trigger calls to the Serialize and GetSerializedSize + * methods of each tag stored in this packet. + * + * This method will typically be used by parallel simulations where + * the simulated system is partitioned and each partition runs on + * a different CPU. + */ + Buffer Serialize (void) const; + /** + * \param buffer a byte buffer + * + * This method reads a byte buffer as created by Packet::Serialize + * and restores the state of the Packet to what it was prior to + * calling Serialize. + * + * This method will trigger calls to the Deserialize method + * of each tag stored in this packet. + * + * This method will typically be used by parallel simulations where + * the simulated system is partitioned and each partition runs on + * a different CPU. + */ + void Deserialize (Buffer buffer); private: Packet (Buffer buffer, Tags tags, PacketMetadata metadata); Buffer m_buffer; @@ -302,7 +328,42 @@ private: static uint32_t m_globalUid; }; -}; // namespace ns3 +/** + * \defgroup packetperf Packet Performance + * The current implementation of the byte buffers and tag list is based + * on COW (Copy On Write. An introduction to COW can be found in Scott + * Meyer's "More Effective C++", items 17 and 29). What this means is that + * copying packets without modifying them is very cheap (in terms of cpu + * and memory usage) and modifying them can be also very cheap. What is + * key for proper COW implementations is being + * able to detect when a given modification of the state of a packet triggers + * a full copy of the data prior to the modification: COW systems need + * to detect when an operation is "dirty". + * + * Dirty operations: + * - ns3::Packet::RemoveTag + * - ns3::Packet::AddHeader + * - ns3::Packet::AddTrailer + * - both versions of ns3::Packet::AddAtEnd + * + * Non-dirty operations: + * - ns3::Packet::AddTag + * - ns3::Packet::RemoveAllTags + * - ns3::Packet::PeekTag + * - ns3::Packet::RemoveHeader + * - ns3::Packet::RemoveTrailer + * - ns3::Packet::CreateFragment + * - ns3::Packet::RemoveAtStart + * - ns3::Packet::RemoveAtEnd + * + * Dirty operations will always be slower than non-dirty operations, + * sometimes by several orders of magnitude. However, even the + * dirty operations have been optimized for common use-cases which + * means that most of the time, these operations will not trigger + * data copies and will thus be still very fast. + */ + +} // namespace ns3 /************************************************** @@ -316,9 +377,11 @@ template void Packet::AddHeader (T const &header) { - NS_ASSERT_MSG (dynamic_cast
(&header) != 0, - "Must pass Header subclass to Packet::AddHeader"); - uint32_t size = header.GetSize (); + const Header *testHeader; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Header class + testHeader = &header; + uint32_t size = header.GetSerializedSize (); m_buffer.AddAtStart (size); header.Serialize (m_buffer.Begin ()); m_metadata.AddHeader (header, size); @@ -327,8 +390,10 @@ template uint32_t Packet::RemoveHeader (T &header) { - NS_ASSERT_MSG (dynamic_cast
(&header) != 0, - "Must pass Header subclass to Packet::RemoveHeader"); + Header *testHeader; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Header class + testHeader = &header; uint32_t deserialized = header.Deserialize (m_buffer.Begin ()); m_buffer.RemoveAtStart (deserialized); m_metadata.RemoveHeader (header, deserialized); @@ -338,9 +403,11 @@ template void Packet::AddTrailer (T const &trailer) { - NS_ASSERT_MSG (dynamic_cast (&trailer) != 0, - "Must pass Trailer subclass to Packet::AddTrailer"); - uint32_t size = trailer.GetSize (); + const Trailer *testTrailer; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Trailer class + testTrailer = &trailer; + uint32_t size = trailer.GetSerializedSize (); m_buffer.AddAtEnd (size); Buffer::Iterator end = m_buffer.End (); trailer.Serialize (end); @@ -350,8 +417,10 @@ template uint32_t Packet::RemoveTrailer (T &trailer) { - NS_ASSERT_MSG (dynamic_cast (&trailer) != 0, - "Must pass Trailer subclass to Packet::RemoveTrailer"); + Trailer *testTrailer; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Trailer class + testTrailer = &trailer; uint32_t deserialized = trailer.Deserialize (m_buffer.End ()); m_buffer.RemoveAtEnd (deserialized); m_metadata.RemoveTrailer (trailer, deserialized); @@ -362,18 +431,31 @@ Packet::RemoveTrailer (T &trailer) template void Packet::AddTag (T const& tag) { + const Tag *parent; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Tag class. + parent = &tag; m_tags.Add (tag); } template bool Packet::RemoveTag (T & tag) { + Tag *parent; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Tag class. + parent = &tag; return m_tags.Remove (tag); } template bool Packet::PeekTag (T & tag) const { + Tag *parent; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Tag class. + parent = &tag; return m_tags.Peek (tag); } -}; // namespace ns3 + +} // namespace ns3 #endif /* PACKET_H */ diff --git a/src/common/tag-registry.cc b/src/common/tag-registry.cc new file mode 100644 index 000000000..93778a9f7 --- /dev/null +++ b/src/common/tag-registry.cc @@ -0,0 +1,87 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 INRIA + * All rights reserved. + * + * 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: Mathieu Lacage + */ +#include "tag-registry.h" +#include "ns3/fatal-error.h" + +namespace ns3 { + +TagRegistry::TagInfoVector * +TagRegistry::GetInfo (void) +{ + static TagRegistry::TagInfoVector vector; + return &vector; +} + +std::string +TagRegistry::GetUidString (uint32_t uid) +{ + TagInfo info = (*GetInfo ())[uid - 1]; + return info.uidString; +} +uint32_t +TagRegistry::GetUidFromUidString (std::string uidString) +{ + TagInfoVector *vec = GetInfo (); + uint32_t uid = 1; + for (TagInfoVector::iterator i = vec->begin (); i != vec->end (); i++) + { + if (i->uidString == uidString) + { + return uid; + } + uid++; + } + NS_FATAL_ERROR ("We are trying to deserialize an un-registered type. This can't work."); + return 0; +} + +void +TagRegistry::Destruct (uint32_t uid, uint8_t *data) +{ + TagInfo info = (*GetInfo ())[uid - 1]; + info.destruct (data); +} +void +TagRegistry::Print (uint32_t uid, uint8_t *data, std::ostream &os) +{ + TagInfo info = (*GetInfo ())[uid - 1]; + info.print (data, os); +} +uint32_t +TagRegistry::GetSerializedSize (uint32_t uid, uint8_t *data) +{ + TagInfo info = (*GetInfo ())[uid - 1]; + return info.getSerializedSize (data); +} +void +TagRegistry::Serialize (uint32_t uid, uint8_t *data, Buffer::Iterator start) +{ + TagInfo info = (*GetInfo ())[uid - 1]; + info.serialize (data, start); +} +uint32_t +TagRegistry::Deserialize (uint32_t uid, uint8_t *data, Buffer::Iterator start) +{ + TagInfo info = (*GetInfo ())[uid - 1]; + return info.deserialize (data, start); +} + +} // namespace ns3 diff --git a/src/common/tag-registry.h b/src/common/tag-registry.h new file mode 100644 index 000000000..47cb29128 --- /dev/null +++ b/src/common/tag-registry.h @@ -0,0 +1,149 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 INRIA + * All rights reserved. + * + * 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: Mathieu Lacage + */ +#ifndef TAG_REGISTRY_H +#define TAG_REGISTRY_H + +#include +#include +#include "buffer.h" + +namespace ns3 { + +/** + * \brief a registry of all existing tag types. + * \internal + * + * This class is used to give polymorphic access to the methods + * exported by a tag. It also is used to associate a single + * reliable uid to each unique type. + */ +class TagRegistry +{ +public: + template + static uint32_t Register (std::string uidString); + static std::string GetUidString (uint32_t uid); + static uint32_t GetUidFromUidString (std::string uidString); + static void Destruct (uint32_t uid, uint8_t *data); + static void Print (uint32_t uid, uint8_t *data, std::ostream &os); + static uint32_t GetSerializedSize (uint32_t uid, uint8_t *data); + static void Serialize (uint32_t uid, uint8_t *data, Buffer::Iterator start); + static uint32_t Deserialize (uint32_t uid, uint8_t *data, Buffer::Iterator start); +private: + typedef void (*DestructCb) (uint8_t *); + typedef void (*PrintCb) (uint8_t *, std::ostream &); + typedef uint32_t (*GetSerializedSizeCb) (uint8_t *); + typedef void (*SerializeCb) (uint8_t *, Buffer::Iterator); + typedef uint32_t (*DeserializeCb) (uint8_t *, Buffer::Iterator); + struct TagInfo + { + std::string uidString; + DestructCb destruct; + PrintCb print; + GetSerializedSizeCb getSerializedSize; + SerializeCb serialize; + DeserializeCb deserialize; + }; + typedef std::vector TagInfoVector; + + template + static void DoDestruct (uint8_t *data); + template + static void DoPrint (uint8_t *data, std::ostream &os); + template + static uint32_t DoGetSerializedSize (uint8_t *data); + template + static void DoSerialize (uint8_t *data, Buffer::Iterator start); + template + static uint32_t DoDeserialize (uint8_t *data, Buffer::Iterator start); + + static TagInfoVector *GetInfo (void); +}; + +} // namespace ns3 + +namespace ns3 { + +template +void +TagRegistry::DoDestruct (uint8_t *data) +{ + T *tag = reinterpret_cast (data); + tag->~T (); +} +template +void +TagRegistry::DoPrint (uint8_t *data, std::ostream &os) +{ + T *tag = reinterpret_cast (data); + tag->Print (os); +} +template +uint32_t +TagRegistry::DoGetSerializedSize (uint8_t *data) +{ + T *tag = reinterpret_cast (data); + return tag->GetSerializedSize (); +} +template +void +TagRegistry::DoSerialize (uint8_t *data, Buffer::Iterator start) +{ + T *tag = reinterpret_cast (data); + tag->Serialize (start); +} +template +uint32_t +TagRegistry::DoDeserialize (uint8_t *data, Buffer::Iterator start) +{ + T *tag = reinterpret_cast (data); + return tag->Deserialize (start); +} + +template +uint32_t +TagRegistry::Register (std::string uidString) +{ + TagInfoVector *vec = GetInfo (); + uint32_t j = 0; + for (TagInfoVector::iterator i = vec->begin (); i != vec->end (); i++) + { + if (i->uidString == uidString) + { + return j; + } + j++; + } + TagInfo info; + info.uidString = uidString; + info.destruct = &TagRegistry::DoDestruct; + info.print = &TagRegistry::DoPrint; + info.getSerializedSize = &TagRegistry::DoGetSerializedSize; + info.serialize = &TagRegistry::DoSerialize; + info.deserialize = &TagRegistry::DoDeserialize; + vec->push_back (info); + uint32_t uid = vec->size (); + return uid; +} + +} // namespace ns3 + +#endif /* TAG_REGISTRY_H */ diff --git a/src/common/tag.h b/src/common/tag.h new file mode 100644 index 000000000..f85e797ab --- /dev/null +++ b/src/common/tag.h @@ -0,0 +1,126 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 INRIA + * All rights reserved. + * + * 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: Mathieu Lacage + */ +#ifndef TAG_H +#define TAG_H + +#include +#include + +/** + * \relates ns3::Tag + * \brief this macro should be instantiated exactly once for each + * new type of Tag + * + * This macro will ensure that your new Tag type is registered + * within the tag registry. In most cases, this macro + * is not really needed but, for safety, please, use it all the + * time. + * + * Note: This macro is _absolutely_ needed if you try to run a + * distributed simulation. + */ +#define NS_TAG_ENSURE_REGISTERED(x) \ +static class thisisaveryverylongclassname ##x \ +{ \ + public: \ + thisisaveryverylongclassname ##x () \ + { uint32_t uid; uid = x::GetUid ();} \ +} g_thisisanotherveryveryverylongname ## x; + +namespace ns3 { + +/** + * \brief a tag can be stored in a packet. + * + * A tag is a blob of 16 bytes of data which can be stored in + * a packet: a packet can contain an arbitrary number of tags + * and these tags are considered as "on-the-side" per-packet + * data structures which are not taken into account when calculating + * the size of the payload of a packet. They exist solely as + * simulation-specific objects. + * + * Tags are typically used to: + * - implement per-packet cross-layer communication + * - implement packet coloring: you could store a "color" tag + * in a packet to mark various types of packet for + * simulation analysis + * + * To create a new type of tag, you must create a subclass + * of the Tag base class which defines: + * - a public default constructor: needed for implementation + * purposes of the Packet code. + * - a public copy constructor: needed to copy a tag into + * a packet tag buffer when the user invokes Packet::AddTag + * - a public destructor: needed to destroy the copy of a tag + * stored in a packet buffer when the user invokes Packet::RemoveTag + * or when the packet is destroyed and the last reference to + * a tag instance disapears. + * - a public static method named GetUid: needed to uniquely + * the type of each tag instance. + * - a public method named Print: needed to print the content + * of a tag when the user calls Packet::PrintTags + * - a public method named GetSerializedSize: needed to serialize + * the content of a tag to a byte buffer when a packet must + * be sent from one computing node to another in a parallel + * simulation. If this method returns 0, it means that the + * tag does not need to be transfered from computing node to + * computing node + * - a public method named Serialize: perform the serialization + * to a byte buffer upon transfer to a new computing node in a + * parallel simulation. + * - a public method named Deserialize: invert the serialization + * from a byte buffer after being transfered to a new computing + * node in a parallel simulation. + * + * A detailed example of what these methods should look like + * and how they should be implemented is described in samples/main-packet-tag.cc + */ +class Tag +{ +protected: + /** + * \param name the unique name of the new type of tag + * \returns a newly-allocated uid + * + * This method should be used by subclasses to implement + * their static public GetUid method. + */ + template + static uint32_t AllocateUid (std::string name); +}; + +} // namespace ns3 + +// implementation below. +#include "tag-registry.h" + +namespace ns3 { + +template +uint32_t +Tag::AllocateUid (std::string name) +{ + return TagRegistry::Register (name); +} + +} // namespace ns3 + +#endif /* TAG_H */ diff --git a/src/common/tags.cc b/src/common/tags.cc index 2d72f6bec..e2206b7af 100644 --- a/src/common/tags.cc +++ b/src/common/tags.cc @@ -20,84 +20,10 @@ */ #include "tags.h" #include +#include "ns3/fatal-error.h" namespace ns3 { -TagRegistry * -TagRegistry::GetInstance (void) -{ - static TagRegistry registry; - return ®istry; -} - -TagRegistry::TagRegistry () - : m_sorted (false) -{} - - -void -TagRegistry::Record (std::string uuid, PrettyPrinter prettyPrinter, Destructor destructor) -{ - NS_ASSERT (!m_sorted); - struct TagInfoItem item; - item.uuid = uuid; - item.printer = prettyPrinter; - item.destructor = destructor; - m_registry.push_back (item); -} -bool -TagRegistry::CompareItem (const struct TagInfoItem &a, const struct TagInfoItem &b) -{ - return a.uuid < b.uuid; -} -uint32_t -TagRegistry::LookupUid (std::string uuid) -{ - if (!m_sorted) - { - std::sort (m_registry.begin (), m_registry.end (), &TagRegistry::CompareItem); - m_sorted = true; - } - NS_ASSERT (m_sorted); - uint32_t uid = 1; - for (TagsDataCI i = m_registry.begin (); i != m_registry.end (); i++) - { - if (i->uuid == uuid) - { - return uid; - } - uid++; - } - // someone asked for a uid for an unregistered uuid. - NS_ASSERT (!"You tried to use unregistered tag: make sure you create an instance of type TagRegistration."); - // quiet compiler - return 0; -} -void -TagRegistry::PrettyPrint (uint32_t uid, uint8_t buf[Tags::SIZE], std::ostream &os) -{ - NS_ASSERT (uid > 0); - uint32_t index = uid - 1; - NS_ASSERT (m_registry.size () > index); - PrettyPrinter prettyPrinter = m_registry[index].printer; - if (prettyPrinter != 0) - { - prettyPrinter (buf, os); - } -} -void -TagRegistry::Destruct (uint32_t uid, uint8_t buf[Tags::SIZE]) -{ - NS_ASSERT (uid > 0); - uint32_t index = uid - 1; - NS_ASSERT (m_registry.size () > index); - Destructor destructor = m_registry[index].destructor; - NS_ASSERT (destructor != 0); - destructor (buf); -} - - - #ifdef USE_FREE_LIST struct Tags::TagData *Tags::gFree = 0; @@ -193,14 +119,103 @@ Tags::Remove (uint32_t id) } void -Tags::PrettyPrint (std::ostream &os) +Tags::Print (std::ostream &os, std::string separator) const { for (struct TagData *cur = m_next; cur != 0; cur = cur->m_next) { - TagRegistry::GetInstance ()->PrettyPrint (cur->m_id, cur->m_data, os); + TagRegistry::Print (cur->m_id, cur->m_data, os); + if (cur->m_next != 0) + { + os << separator; + } } } +uint32_t +Tags::GetSerializedSize (void) const +{ + uint32_t totalSize = 4; // reserve space for the size of the tag data. + for (struct TagData *cur = m_next; cur != 0; cur = cur->m_next) + { + uint32_t size = TagRegistry::GetSerializedSize (cur->m_id, cur->m_data); + if (size != 0) + { + std::string uidString = TagRegistry::GetUidString (cur->m_id); + totalSize += 4; // for the size of the string itself. + totalSize += uidString.size (); + totalSize += size; + } + } + return totalSize; +} + +void +Tags::Serialize (Buffer::Iterator i, uint32_t totalSize) const +{ + i.WriteU32 (totalSize); + for (struct TagData *cur = m_next; cur != 0; cur = cur->m_next) + { + uint32_t size = TagRegistry::GetSerializedSize (cur->m_id, cur->m_data); + if (size != 0) + { + std::string uidString = TagRegistry::GetUidString (cur->m_id); + i.WriteU32 (uidString.size ()); + uint8_t *buf = (uint8_t *)uidString.c_str (); + i.Write (buf, uidString.size ()); + TagRegistry::Serialize (cur->m_id, cur->m_data, i); + } + } +} +uint32_t +Tags::Deserialize (Buffer::Iterator i) +{ + uint32_t totalSize = i.ReadU32 (); + uint32_t bytesRead = 4; + while (bytesRead < totalSize) + { + uint32_t uidStringSize = i.ReadU32 (); + bytesRead += 4; + std::string uidString; + uidString.reserve (uidStringSize); + for (uint32_t j = 0; j < uidStringSize; j++) + { + uint32_t c = i.ReadU8 (); + uidString.push_back (c); + } + bytesRead += uidStringSize; + uint32_t uid = TagRegistry::GetUidFromUidString (uidString); + struct TagData *newStart = AllocData (); + newStart->m_count = 1; + newStart->m_next = 0; + newStart->m_id = uid; + bytesRead += TagRegistry::Deserialize (uid, newStart->m_data, i); + newStart->m_next = m_next; + m_next = newStart; + } + NS_ASSERT (bytesRead == totalSize); + /** + * The process of serialization/deserialization + * results in an inverted linked-list after + * deserialization so, we invert the linked-list + * in-place here. + * Note: the algorithm below is pretty classic + * but whenever I get to code it, it makes my + * brain hurt :) + */ + struct TagData *prev = 0; + struct TagData *cur = m_next; + while (cur != 0) + { + struct TagData *next = cur->m_next; + cur->m_next = prev; + prev = cur; + cur = next; + } + m_next = prev; + return totalSize; +} + + }; // namespace ns3 @@ -224,25 +239,67 @@ public: virtual bool RunTests (void); }; -struct myTagA { +class myTagA : public Tag +{ +public: + static uint32_t GetUid (void) {static uint32_t uid = AllocateUid ("myTagA.test.nsnam.org"); return uid;} + void Print (std::ostream &os) const {g_a = true;} + uint32_t GetSerializedSize (void) const {return 1;} + void Serialize (Buffer::Iterator i) const {i.WriteU8 (a);} + uint32_t Deserialize (Buffer::Iterator i) {a = i.ReadU8 (); return 1;} + uint8_t a; }; -struct myTagB { +class myTagB : public Tag +{ +public: + static uint32_t GetUid (void) {static uint32_t uid = AllocateUid ("myTagB.test.nsnam.org"); return uid;} + void Print (std::ostream &os) const {g_b = true;} + uint32_t GetSerializedSize (void) const {return 4;} + void Serialize (Buffer::Iterator i) const {i.WriteU32 (b);} + uint32_t Deserialize (Buffer::Iterator i) {b = i.ReadU32 (); return 4;} + uint32_t b; }; -struct myTagC { +class myTagC : public Tag +{ +public: + static uint32_t GetUid (void) {static uint32_t uid = AllocateUid ("myTagC.test.nsnam.org"); return uid;} + void Print (std::ostream &os) const {g_c = true;} + uint32_t GetSerializedSize (void) const {return Tags::SIZE;} + void Serialize (Buffer::Iterator i) const {i.Write (c, Tags::SIZE);} + uint32_t Deserialize (Buffer::Iterator i) {i.Read (c, Tags::SIZE); return Tags::SIZE;} uint8_t c [Tags::SIZE]; }; -struct myInvalidTag { +class myInvalidTag : public Tag +{ +public: + static uint32_t GetUid (void) + {static uint32_t uid = AllocateUid ("myinvalidTag.test.nsnam.org"); return uid;} + void Print (std::ostream &os) const {} + uint32_t GetSerializedSize (void) const {return 0;} + void Serialize (Buffer::Iterator i) const {} + uint32_t Deserialize (Buffer::Iterator i) {return 0;} + uint8_t invalid [Tags::SIZE+1]; }; -struct myTagZ { +class myTagZ : public Tag +{ +public: + static uint32_t GetUid (void) {static uint32_t uid = AllocateUid ("myTagZ.test.nsnam.org"); return uid;} + void Print (std::ostream &os) const {g_z = true;} + uint32_t GetSerializedSize (void) const {return 0;} + void Serialize (Buffer::Iterator i) const {} + uint32_t Deserialize (Buffer::Iterator i) {return 0;} + uint8_t z; }; -class MySmartTag +class MySmartTag : public Tag { public: + static uint32_t GetUid (void) + {static uint32_t uid = AllocateUid ("MySmartTag.test.nsnam.org"); return uid;} MySmartTag () { //std::cout << "construct" << std::endl; @@ -260,43 +317,12 @@ public: //std::cout << "assign" << std::endl; return *this; } - static void PrettyPrinterCb (const MySmartTag *a, std::ostream &os) - {} + void Print (std::ostream &os) const {} + uint32_t GetSerializedSize (void) const {return 0;} + void Serialize (Buffer::Iterator i) const {} + uint32_t Deserialize (Buffer::Iterator i) {return 0;} }; -static void -myTagAPrettyPrinterCb (struct myTagA const*a, std::ostream &os) -{ - //os << "struct myTagA, a="<<(uint32_t)a->a< gMyTagARegistration ("A", &myTagAPrettyPrinterCb); -static TagRegistration gMyTagBRegistration ("B", &myTagBPrettyPrinterCb); -static TagRegistration gMyTagCRegistration ("C", &myTagCPrettyPrinterCb); -static TagRegistration g_myTagZRegistration ("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", - &myTagZPrettyPrinterCb); -static TagRegistration g_myTagSmartRegistration ("SmartTag", &MySmartTag::PrettyPrinterCb); - TagsTest::TagsTest () : Test ("Tags") @@ -311,7 +337,7 @@ TagsTest::RunTests (void) // build initial tag. Tags tags; - struct myTagA a; + myTagA a; a.a = 10; tags.Add (a); a.a = 0; @@ -321,12 +347,12 @@ TagsTest::RunTests (void) ok = false; } g_a = false; - tags.PrettyPrint (std::cout); + tags.Print (std::cout, ""); if (!g_a) { ok = false; } - struct myTagB b; + myTagB b; b.b = 0xff; tags.Add (b); b.b = 0; @@ -337,7 +363,7 @@ TagsTest::RunTests (void) } g_b = false; g_a = false; - tags.PrettyPrint (std::cout); + tags.Print (std::cout, ""); if (!g_a || !g_b) { ok = false; @@ -346,26 +372,26 @@ TagsTest::RunTests (void) Tags other = tags; g_b = false; g_a = false; - other.PrettyPrint (std::cout); + other.Print (std::cout, ""); if (!g_a || !g_b) { ok = false; } g_b = false; g_a = false; - tags.PrettyPrint (std::cout); + tags.Print (std::cout, ""); if (!g_a || !g_b) { ok = false; } - struct myTagA oA; + myTagA oA; oA.a = 0; other.Peek (oA); if (oA.a != 10) { ok = false; } - struct myTagB oB; + myTagB oB; oB.b = 1; other.Peek (oB); if (oB.b != 0xff) @@ -380,7 +406,7 @@ TagsTest::RunTests (void) } g_b = false; g_a = false; - other.PrettyPrint (std::cout); + other.Print (std::cout, ""); if (g_a || !g_b) { ok = false; @@ -401,7 +427,7 @@ TagsTest::RunTests (void) other = tags; Tags another = other; - struct myTagC c; + myTagC c; memset (c.c, 0x66, 16); another.Add (c); c.c[0] = 0; @@ -421,12 +447,12 @@ TagsTest::RunTests (void) //struct myInvalidTag invalid; //tags.add (&invalid); - struct myTagZ tagZ; + myTagZ tagZ; Tags testLastTag; tagZ.z = 0; testLastTag.Add (tagZ); g_z = false; - testLastTag.PrettyPrint (std::cout); + testLastTag.Print (std::cout, ""); if (!g_z) { ok = false; @@ -440,6 +466,58 @@ TagsTest::RunTests (void) tmp.Remove (smartTag); } + { + Tags source; + myTagA aSource; + aSource.a = 0x66; + source.Add (aSource); + Buffer buffer; + uint32_t serialized = source.GetSerializedSize (); + buffer.AddAtStart (serialized); + source.Serialize (buffer.Begin (), serialized); + Tags dest; + dest.Deserialize (buffer.Begin ()); + myTagA aDest; + aDest.a = 0x55; + dest.Peek (aDest); + if (aDest.a != 0x66) + { + ok = false; + } + } + + { + Tags source; + myTagA aSource; + aSource.a = 0x66; + source.Add (aSource); + myTagZ zSource; + zSource.z = 0x77; + source.Add (zSource); + + Buffer buffer; + uint32_t serialized = source.GetSerializedSize (); + buffer.AddAtStart (serialized); + source.Serialize (buffer.Begin (), serialized); + Tags dest; + dest.Deserialize (buffer.Begin ()); + + myTagA aDest; + aDest.a = 0x55; + dest.Peek (aDest); + if (aDest.a != 0x66) + { + ok = false; + } + myTagZ zDest; + zDest.z = 0x44; + dest.Peek (zDest); + if (zDest.z != 0x44) + { + ok = false; + } + } + return ok; } diff --git a/src/common/tags.h b/src/common/tags.h index 3c9583c9a..4002ae4f8 100644 --- a/src/common/tags.h +++ b/src/common/tags.h @@ -24,12 +24,10 @@ #include #include #include +#include "buffer.h" namespace ns3 { -template -class TagPrettyPrinter; - /** * \ingroup constants * \brief Tag maximum size @@ -54,7 +52,10 @@ public: template bool Peek (T &tag) const; - void PrettyPrint (std::ostream &os); + void Print (std::ostream &os, std::string separator) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator i, uint32_t size) const; + uint32_t Deserialize (Buffer::Iterator i); inline void RemoveAll (void); @@ -79,161 +80,39 @@ private: struct TagData *m_next; }; -/** - * \brief pretty print packet tags - * - * This class is used to register a pretty-printer - * callback function to print in a nice user-friendly - * way the content of the target type. To register - * such a type, all you need to do is instantiate - * an instance of this type as a static variable. - */ -template -class TagRegistration { -public: - /** - * \param uuid a uuid generated with uuidgen - * \param fn a function which can pretty-print an instance - * of type T in the output stream. - */ - TagRegistration (std::string uuid, void(*fn) (T const*, std::ostream &)); -private: - static void PrettyPrinterCb (uint8_t *buf, std::ostream &os); - static void DestructorCb (uint8_t *buf); - static void(*m_prettyPrinter) (T const*, std::ostream &); -}; - -}; // namespace ns3 +} // namespace ns3 /************************************************************** An implementation of the templates defined above *************************************************************/ +#include "tag-registry.h" +#include "tag.h" #include "ns3/assert.h" #include namespace ns3 { -class TagRegistry { -public: - typedef void (*PrettyPrinter) (uint8_t [Tags::SIZE], std::ostream &); - typedef void (*Destructor) (uint8_t [Tags::SIZE]); - void Record (std::string uuid, PrettyPrinter prettyPrinter, Destructor destructor); - /** - * returns a numeric integer which uniquely identifies the input string. - * that integer cannot be zero which is a reserved value. - */ - uint32_t LookupUid (std::string uuid); - void PrettyPrint (uint32_t uid, uint8_t buf[Tags::SIZE], std::ostream &os); - void Destruct (uint32_t uid, uint8_t buf[Tags::SIZE]); - - static TagRegistry *GetInstance (void); -private: - TagRegistry (); - struct TagInfoItem - { - std::string uuid; - PrettyPrinter printer; - Destructor destructor; - }; - typedef std::vector TagsData; - typedef std::vector::const_iterator TagsDataCI; - static bool CompareItem (const struct TagInfoItem &a, const struct TagInfoItem &b); - bool m_sorted; - TagsData m_registry; -}; -/** - * The TypeUid class is used to create a mapping Type --> uid - * Note that we use a static getUuid function which contains a - * static std::string variable rather than a simpler static - * member std::string variable to ensure the proper order - * of initialization when these methods are invoked - * from the constructor of another static variable. - */ -template -class TypeUid { -public: - static void Record (std::string uuid); - static const uint32_t GetUid (void); -private: - static std::string *GetUuid (void); - T m_realType; -}; - -template -void TypeUid::Record (std::string uuid) -{ - *(GetUuid ()) = uuid; -} - -template -const uint32_t TypeUid::GetUid (void) -{ - static const uint32_t uid = TagRegistry::GetInstance ()-> - LookupUid (*(GetUuid ())); - return uid; -} - -template -std::string *TypeUid::GetUuid (void) -{ - static std::string uuid; - return &uuid; -} - - - -/** - * Implementation of the TagRegistration registration class. - * It records a callback with the TagRegistry - * This callback performs type conversion before forwarding - * the call to the user-provided function. - */ -template -TagRegistration::TagRegistration (std::string uuid, void (*prettyPrinter) (T const*, std::ostream &)) -{ - NS_ASSERT (sizeof (T) <= Tags::SIZE); - m_prettyPrinter = prettyPrinter; - TagRegistry::GetInstance ()-> - Record (uuid, &TagRegistration::PrettyPrinterCb, &TagRegistration::DestructorCb); - TypeUid::Record (uuid); -} -template -void -TagRegistration::PrettyPrinterCb (uint8_t *buf, std::ostream &os) -{ - NS_ASSERT (sizeof (T) <= Tags::SIZE); - T *tag = reinterpret_cast (buf); - (*m_prettyPrinter) (tag, os); -} -template -void -TagRegistration::DestructorCb (uint8_t *buf) -{ - T *tag = reinterpret_cast (buf); - tag->~T (); -} -template -void (*TagRegistration::m_prettyPrinter) (T const*, std::ostream &) = 0; - - - - template void Tags::Add (T const&tag) { + const Tag *parent; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Tag class. + parent = &tag; + NS_ASSERT (sizeof (T) <= Tags::SIZE); // ensure this id was not yet added for (struct TagData *cur = m_next; cur != 0; cur = cur->m_next) { - NS_ASSERT (cur->m_id != TypeUid::GetUid ()); + NS_ASSERT (cur->m_id != T::GetUid ()); } struct TagData *newStart = AllocData (); newStart->m_count = 1; newStart->m_next = 0; - newStart->m_id = TypeUid::GetUid (); + newStart->m_id = T::GetUid (); void *buf = &newStart->m_data; new (buf) T (tag); newStart->m_next = m_next; @@ -244,18 +123,26 @@ template bool Tags::Remove (T &tag) { + Tag *parent; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Tag class. + parent = &tag; NS_ASSERT (sizeof (T) <= Tags::SIZE); - return Remove (TypeUid::GetUid ()); + return Remove (T::GetUid ()); } template bool Tags::Peek (T &tag) const { + Tag *parent; + // if the following assignment fails, it is because the + // input to this function is not a subclass of the Tag class. + parent = &tag; NS_ASSERT (sizeof (T) <= Tags::SIZE); for (struct TagData *cur = m_next; cur != 0; cur = cur->m_next) { - if (cur->m_id == TypeUid::GetUid ()) + if (cur->m_id == T::GetUid ()) { /* found tag */ T *data = reinterpret_cast (&cur->m_data); @@ -315,16 +202,14 @@ Tags::RemoveAll (void) } if (prev != 0) { - TagRegistry::GetInstance ()-> - Destruct (prev->m_id, prev->m_data); + TagRegistry::Destruct (prev->m_id, prev->m_data); FreeData (prev); } prev = cur; } if (prev != 0) { - TagRegistry::GetInstance ()-> - Destruct (prev->m_id, prev->m_data); + TagRegistry::Destruct (prev->m_id, prev->m_data); FreeData (prev); } m_next = 0; diff --git a/src/common/trace-context-element.cc b/src/common/trace-context-element.cc new file mode 100644 index 000000000..838250af8 --- /dev/null +++ b/src/common/trace-context-element.cc @@ -0,0 +1,34 @@ +#include "trace-context-element.h" + +namespace ns3 { + +uint32_t +ElementRegistry::GetSize (uint16_t uid) +{ + InfoVector *vec = GetInfoVector (); + struct Info info = (*vec)[uid - 1]; + return info.size; +} +void +ElementRegistry::Print (uint16_t uid, uint8_t *instance, std::ostream &os) +{ + InfoVector *vec = GetInfoVector (); + struct Info info = (*vec)[uid - 1]; + info.print (instance, os); +} +void +ElementRegistry::Destroy (uint16_t uid, uint8_t *instance) +{ + InfoVector *vec = GetInfoVector (); + struct Info info = (*vec)[uid - 1]; + info.destroy (instance); +} +ElementRegistry::InfoVector * +ElementRegistry::GetInfoVector (void) +{ + static InfoVector vector; + return &vector; +} + + +} // namespace ns3 diff --git a/src/common/trace-context-element.h b/src/common/trace-context-element.h new file mode 100644 index 000000000..e1c36c4d9 --- /dev/null +++ b/src/common/trace-context-element.h @@ -0,0 +1,189 @@ +#ifndef TRACE_CONTEXT_ELEMENT_H +#define TRACE_CONTEXT_ELEMENT_H + +#include +#include + +#define NS_TRACE_CONTEXT_ELEMENT_ENSURE_REGISTERED(x) \ +namespace { \ +static class thisisaveryverylongclassname ##x \ + { \ + public: \ + thisisaveryverylongclassname ##x () \ + { uint32_t uid; uid = x::GetUid ();} \ + } g_thisisanotherveryveryverylongname ##x ; \ +} + +namespace ns3 { + +/** + * \brief an item stored in a TraceContext + * + * To store trace context information in a TraceContext instance, + * users must subclass this base class and store subclass instances + * in a TraceContext with TraceContext::Add. + * + * Each subclass should define and implement: + * - a public default constructor: it is used by the internals + * of the implementation of TraceContext. + * - a public destructor: it is also used by the internals of + * the implementation of TraceContext. + * - a public static method named GetUid which returns a 16 bit + * integer. The integer returned from this method should be + * allocated with the protected AllocatedUid method. + * - a public Print method: this method is used by the + * TraceContext::Print method to print the content of each + * of the trace context element stored in the trace context. + * This method takes a c++ output stream and argument and is + * expected to write an ascii string describing its content + * in this output stream. + * + * A typical subclass should look like this: + * \code + * class MyContext : public TraceContextElement + * { + * public: + * // the _required_ public API + * static uint16_t GetUid (void); + * MyContext (); + * ~MyContext (); + * void Print (std::ostream &os) const; + * + * // the user-specific API to manipulate the context. + * void SetData (uint8_t data); + * uint8_t GetData (void) const; + * private: + * uint8_t m_myContextData; + * }; + * + * uint16_t + * MyContext::GetUid (void) + * { + * static uint16_t uid = AllocateUid ("MyContext"); + * return uid; + * } + * MyContext::MyContext () + * {} + * MyContext::~MyContext () + * {} + * void + * MyContext::Print (std::ostream &os) const + * { + * os << "mycontext=" << (uint32_t) m_myContextData; + * } + * void + * MyContext::SetData (uint8_t data) + * { + * m_myContextData = data; + * } + * uint8_t + * MyContext::GetData (void) const + * { + * return m_myContextData; + * } + * \endcode + */ +class TraceContextElement +{ +protected: + /** + * \param name a string which uniquely identifies the type + * of the subclass which is calling this method. + * \returns a unique 32 bit integer associated to the + * input string. + * + * Subclasses are expected to call this method from their + * public static GetUid method. + */ + template + static uint16_t AllocateUid (std::string name); +}; + +} // namespace ns3 + +namespace ns3 { + +/** + * \brief a registry of TraceContextElement subclasses + * \internal + */ +class ElementRegistry +{ +public: + template + static uint16_t AllocateUid (std::string name); + + static uint32_t GetSize (uint16_t uid); + static void Print (uint16_t uid, uint8_t *instance, std::ostream &os); + static void Destroy (uint16_t uid, uint8_t *instance); +private: + typedef void (*PrintCb) (uint8_t *instance, std::ostream &os); + typedef void (*DestroyCb) (uint8_t *instance); + struct Info { + uint32_t size; + std::string uidString; + PrintCb print; + DestroyCb destroy; + }; + typedef std::vector InfoVector; + static InfoVector *GetInfoVector (void); + template + static void DoPrint (uint8_t *instance, std::ostream &os); + template + static void DoDestroy (uint8_t *instance); +}; + +template +void +ElementRegistry::DoPrint (uint8_t *instance, std::ostream &os) +{ + static T obj; + // make sure we are aligned. + memcpy ((void*)&obj, instance, sizeof (T)); + obj.Print (os); +} +template +void +ElementRegistry::DoDestroy (uint8_t *instance) +{ + static T obj; + // make sure we are aligned. + memcpy ((void*)&obj, instance, sizeof (T)); + obj.~T (); +} + +template +uint16_t +ElementRegistry::AllocateUid (std::string name) +{ + InfoVector *vec = GetInfoVector (); + uint16_t uid = 1; + for (InfoVector::iterator i = vec->begin (); i != vec->end (); i++) + { + if (i->uidString == name) + { + return uid; + } + uid++; + } + struct Info info; + info.size = sizeof (T); + info.uidString = name; + info.print = &ElementRegistry::DoPrint; + info.destroy = &ElementRegistry::DoDestroy; + vec->push_back (info); + return vec->size (); +} + + + +template +uint16_t +TraceContextElement::AllocateUid (std::string name) +{ + return ElementRegistry::AllocateUid (name); +} + +} // namespace ns3 + +#endif /* TRACE_CONTEXT_ELEMENT_H */ diff --git a/src/common/trace-context.cc b/src/common/trace-context.cc index 786ce9149..50e9d0a73 100644 --- a/src/common/trace-context.cc +++ b/src/common/trace-context.cc @@ -19,12 +19,11 @@ * Author: Mathieu Lacage */ #include "trace-context.h" +#include "trace-context-element.h" #include "ns3/assert.h" namespace ns3 { -std::vector TraceContext::m_sizes; - TraceContext::TraceContext () : m_data (0) {} @@ -68,12 +67,6 @@ TraceContext::~TraceContext () } } -uint8_t -TraceContext::GetSize (uint8_t uid) -{ - return m_sizes[uid]; -} - void TraceContext::Add (TraceContext const &o) { @@ -81,12 +74,12 @@ TraceContext::Add (TraceContext const &o) { return; } - uint8_t currentUid; + uint16_t currentUid; uint16_t i = 0; while (i < o.m_data->size) { currentUid = o.m_data->data[i]; - uint8_t size = TraceContext::GetSize (currentUid); + uint8_t size = ElementRegistry::GetSize (currentUid); uint8_t *selfBuffer = CheckPresent (currentUid); uint8_t *otherBuffer = &(o.m_data->data[i+1]); if (selfBuffer != 0) @@ -116,7 +109,7 @@ TraceContext::CheckPresent (uint8_t uid) const uint16_t i = 0; do { currentUid = m_data->data[i]; - uint8_t size = TraceContext::GetSize (currentUid); + uint8_t size = ElementRegistry::GetSize (currentUid); if (currentUid == uid) { return &m_data->data[i+1]; @@ -131,7 +124,7 @@ bool TraceContext::DoAdd (uint8_t uid, uint8_t const *buffer) { NS_ASSERT (uid != 0); - uint8_t size = TraceContext::GetSize (uid); + uint8_t size = ElementRegistry::GetSize (uid); uint8_t *present = CheckPresent (uid); if (present != 0) { if (memcmp (present, buffer, size) == 0) @@ -201,7 +194,7 @@ TraceContext::DoGet (uint8_t uid, uint8_t *buffer) const uint16_t i = 0; do { currentUid = m_data->data[i]; - uint8_t size = TraceContext::GetSize (currentUid); + uint8_t size = ElementRegistry::GetSize (currentUid); if (currentUid == uid) { memcpy (buffer, &m_data->data[i+1], size); @@ -212,31 +205,48 @@ TraceContext::DoGet (uint8_t uid, uint8_t *buffer) const return false; } -uint8_t -TraceContext::DoGetNextUid (void) +void +TraceContext::Print (std::ostream &os) const { - static uint8_t uid = 0; - if (uid == 0) + if (m_data == 0) { - m_sizes.push_back (0); + return; } - uid++; - return uid; + uint8_t currentUid; + uint16_t i = 0; + do { + currentUid = m_data->data[i]; + uint8_t size = ElementRegistry::GetSize (currentUid); + uint8_t *instance = &m_data->data[i+1]; + ElementRegistry::Print (currentUid, instance, os); + i += 1 + size; + if (i < m_data->size && currentUid != 0) + { + os << " "; + } + else + { + break; + } + } while (true); } - }//namespace ns3 #include "ns3/test.h" +#include namespace ns3 { template -class Ctx +class Ctx : public TraceContextElement { public: + static uint16_t GetUid (void) {static uint16_t uid = AllocateUid > (GetName ()); return uid;} + static std::string GetName (void) {std::ostringstream oss; oss << "Ctx" << N; return oss.str ();} Ctx () : m_v (0) {} Ctx (int v) : m_v (v) {} + void Print (std::ostream &os) {os << N;} int Get (void) const { return N;} private: int m_v; diff --git a/src/common/trace-context.h b/src/common/trace-context.h index 74108b8da..eee3c960c 100644 --- a/src/common/trace-context.h +++ b/src/common/trace-context.h @@ -24,6 +24,7 @@ #include #include #include "ns3/fatal-error.h" +#include "trace-context-element.h" namespace ns3 { @@ -77,25 +78,20 @@ public: */ template void Get (T &context) const; + + void Print (std::ostream &os) const; private: friend class TraceContextTest; // used exclusively for testing code. template bool SafeGet (T &context) const; template - bool SafeAdd (T &context); + bool SafeAdd (const T &context); - template - static uint8_t GetUid (void); - template - static uint8_t GetNextUid (void); - static uint8_t DoGetNextUid (void); - static uint8_t GetSize (uint8_t uid); uint8_t *CheckPresent (uint8_t uid) const; bool DoAdd (uint8_t uid, uint8_t const *buffer); bool DoGet (uint8_t uid, uint8_t *buffer) const; - static std::vector m_sizes; struct Data { uint16_t count; uint16_t size; @@ -111,8 +107,12 @@ template void TraceContext::Add (T const &context) { + const TraceContextElement *parent; + // if the following assignment fails, it is because the input + // to this function is not a subclass of the TraceContextElement class. + parent = &context; uint8_t *data = (uint8_t *) &context; - bool ok = DoAdd (TraceContext::GetUid (), data); + bool ok = DoAdd (T::GetUid (), data); if (!ok) { NS_FATAL_ERROR ("Trying to add twice the same type with different values is invalid."); @@ -122,8 +122,12 @@ template void TraceContext::Get (T &context) const { + TraceContextElement *parent; + // if the following assignment fails, it is because the input + // to this function is not a subclass of the TraceContextElement class. + parent = &context; uint8_t *data = (uint8_t *) &context; - bool found = DoGet (TraceContext::GetUid (), data); + bool found = DoGet (T::GetUid (), data); if (!found) { NS_FATAL_ERROR ("Type not stored in TraceContext"); @@ -133,35 +137,26 @@ template bool TraceContext::SafeGet (T &context) const { + TraceContextElement *parent; + // if the following assignment fails, it is because the input + // to this function is not a subclass of the TraceContextElement class. + parent = &context; uint8_t *data = (uint8_t *) &context; - bool found = DoGet (TraceContext::GetUid (), data); + bool found = DoGet (T::GetUid (), data); return found; } template bool -TraceContext::SafeAdd (T &context) +TraceContext::SafeAdd (const T &context) { + const TraceContextElement *parent; + // if the following assignment fails, it is because the input + // to this function is not a subclass of the TraceContextElement class. + parent = &context; uint8_t *data = (uint8_t *) &context; - bool ok = DoAdd (TraceContext::GetUid (), data); + bool ok = DoAdd (T::GetUid (), data); return ok; } -template -uint8_t -TraceContext::GetUid (void) -{ - static uint8_t uid = GetNextUid (); - return uid; -} - -template -uint8_t -TraceContext::GetNextUid (void) -{ - uint8_t uid = DoGetNextUid (); - m_sizes.push_back (sizeof (T)); - return uid; -} - }//namespace ns3 #endif /* TRACE_CONTEXT_H */ diff --git a/src/common/trace-root.cc b/src/common/trace-root.cc index 3862408f4..5bb270b8c 100644 --- a/src/common/trace-root.cc +++ b/src/common/trace-root.cc @@ -41,7 +41,7 @@ TraceRoot::Register (std::string name, Callback createResolver) { CompositeTraceResolver *resolver = GetComposite (); - resolver->Add (name, createResolver, TraceRoot::NOTHING); + resolver->Add (name, createResolver); } CompositeTraceResolver * diff --git a/src/common/trace-root.h b/src/common/trace-root.h index 0b2a849a6..a07f923cd 100644 --- a/src/common/trace-root.h +++ b/src/common/trace-root.h @@ -328,9 +328,6 @@ public: Callback createResolver); private: static CompositeTraceResolver *GetComposite (void); - enum TraceType { - NOTHING, - }; }; }// namespace ns3 diff --git a/src/common/trailer.cc b/src/common/trailer.cc deleted file mode 100644 index 8026d2b92..000000000 --- a/src/common/trailer.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2005 INRIA - * All rights reserved. - * - * 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: Mathieu Lacage - */ - -#include "trailer.h" - -namespace ns3 { - -Trailer::~Trailer () -{} - -}; // namespace ns3 diff --git a/src/common/trailer.h b/src/common/trailer.h index 92aef295f..5311d8910 100644 --- a/src/common/trailer.h +++ b/src/common/trailer.h @@ -22,7 +22,28 @@ #ifndef TRAILER_H #define TRAILER_H -#include "chunk.h" +#include "chunk-registry.h" + +/** + * \relates ns3::Trailer + * \brief this macro should be instantiated exactly once for each + * new type of Trailer + * + * This macro will ensure that your new Trailer type is registered + * within the packet trailer registry. In most cases, this macro + * is not really needed but, for safety, please, use it all the + * time. + * + * Note: This macro is _absolutely_ needed if you try to run a + * distributed simulation. + */ +#define NS_TRAILER_ENSURE_REGISTERED(x) \ +static class thisisaveryverylongclassname ##x \ +{ \ + public: \ + thisisaveryverylongclassname ##x () \ + { uint32_t uid; uid = x::GetUid ();} \ +} g_thisisanotherveryveryverylongname ##x; namespace ns3 { @@ -30,98 +51,66 @@ namespace ns3 { * \brief Protocol trailer serialization and deserialization. * * Every Protocol trailer which needs to be inserted or removed - * from a Packet instance must derive from this abstract base class - * and implement the private pure virtual methods listed below: - * - ns3::Trailer::SerializeTo - * - ns3::Trailer::DeserializeFrom - * - ns3::Trailer::GetSerializedSize - * - ns3::Trailer::PrintTo + * from a Packet instance must derive from this base class and + * implement the following public methods: + * - a default constructor: is used by the internal implementation + * if the Packet class. + * - a static method named GetUid: is used to uniquely identify + * the type of each trailer. This method shall return a unique + * integer allocated with Trailer::AllocateUid. + * - a method named Serialize: is used by Packet::AddTrailer to + * store a trailer into the byte buffer of a packet. + * The input iterator points to the end of the byte buffer in + * which the trailer should write its data: the user is thus + * required to call Buffer::Iterator::Prev prior to writing + * any data in the buffer. The data written is expected to + * match bit-for-bit the representation of this trailer in a + * real network. + * - a method named GetSerializedSize: is used by Packet::AddTrailer + * to store a trailer into the byte buffer of a packet. This method + * should return the number of bytes which are needed to store + * the full trailer data by Serialize. + * - a method named Deserialize: is used by Packet::RemoveTrailer to + * re-create a trailer from the byte buffer of a packet. The input + * iterator points to the end of the byte buffer from which + * the trailer should read its data: the user is thus required to + * call Buffer::Iterator::Prev prior to reading any data from the + * buffer. The data read is expected to match bit-for-bit the + * representation of this trailer in real networks. This method + * shall return an integer which identifies the number of bytes read. + * - a method named Print: is used by Packet::Print to print the + * content of a trailer as ascii data to a c++ output stream. + * Although the trailer is free to format its output as it + * wishes, it is recommended to follow a few rules to integrate + * with the packet pretty printer: start with flags, small field + * values located between a pair of parens. Values should be separated + * by whitespace. Follow the parens with the important fields, + * separated by whitespace. + * i.e.: (field1 val1 field2 val2 field3 val3) field4 val4 field5 val5 + * - a method named GetName: is used by Packet::Print to print + * trailer fragments. This method should return a user-readable + * single word as all capitalized letters. * - * Note that the SerializeTo and DeserializeFrom methods behave - * in a way which might seem surprising to users: the input iterator - * really points to the end of the buffer to which and from which - * the user is expected to write and read respectively. This means that - * if the trailer has a fixed size and if the user wishes to read or - * write that trailer from front to back, the user must rewind the - * iterator by hand to go to the start of the trailer. Typical code - * looks like this: - * \code - * void CrcTrailer::SerializeTo (Buffer::Iterator end) - * { - * end.Prev (4); - * end.WriteHtonU32 (m_crc); - * } - * \endcode - * - * Some users would have expected that the iterator would be rewinded - * to the "start" of the trailer before calling SerializeTo and DeserializeFrom. - * However, this behavior was not implemented because it cannot be made to - * work reliably for trailers which have a variable size. i.e., if the trailer - * contains options, the code which calls DeserializeFrom cannot rewind - * to the start of the trailer because it does not know the real size of the - * trailer. Hence, to make this legitimate use-case work (variable-sized - * trailers), the input iterator to DeserializeFrom and SerializeTo points - * to the end of the trailer, and not its start. */ -class Trailer : public Chunk { -public: - virtual ~Trailer (); -private: - /** - * \returns a user-readable name to identify this type of header. - * - * The string returned is expected to be a single word with - * all capital letters - */ - virtual std::string DoGetName (void) const = 0; - /** - * \param os the std output stream in which this - * protocol trailer must print itself. - * - * Although the header is free to format its output as it - * wishes, it is recommended to follow a few rules to integrate - * with the packet pretty printer: - * - start with flags, small field values located between a - * pair of parens. Values should be separated by whitespace. - * - follow the parens with the important fields, separated by - * whitespace. - * i.e.: - * (field1 val1 field2 val2 field3 val3) field4 val4 field5 val5 - */ - virtual void PrintTo (std::ostream &os) const = 0; - - /** - * \returns the size of the serialized Trailer. - * - * This method is used by Packet::AddTrailer to reserve - * enough room in the packet byte buffer prior to calling - * Trailer::Serialize. - */ - virtual uint32_t GetSerializedSize (void) const = 0; - - /** - * \param end the buffer iterator in which the protocol trailer - * must serialize itself. This iterator identifies - * the end of the buffer. - * - * This iterator must be typically moved with the Buffer::Iterator::Prev - * method before writing any byte in the buffer. - */ - virtual void SerializeTo (Buffer::Iterator end) const = 0; - /** - * \param end the buffer iterator from which the protocol trailer must - * deserialize itself. This iterator identifies - * the end of the buffer. - * \returns the number of bytes read from the buffer - * - * This iterator must be typically moved with the Buffer::Iterator::Prev - * method before reading any byte in the buffer. The value returned - * is used to trim the packet byte buffer of the corresponding - * amount when this method is invoked from Packet::RemoveTrailer - */ - virtual uint32_t DeserializeFrom (Buffer::Iterator end) = 0; +class Trailer +{ +protected: + template + static uint32_t AllocateUid (std::string uidString); }; -}; // namespace ns3 +} // namespace ns3 + +namespace ns3 { + +template +uint32_t +Trailer::AllocateUid (std::string uidString) +{ + return ChunkRegistry::RegisterTrailer (uidString); +} + + +} // namespace ns3 #endif /* TRAILER_H */ diff --git a/src/common/wscript b/src/common/wscript index 5d58f37b2..c6e9cb45d 100644 --- a/src/common/wscript +++ b/src/common/wscript @@ -1,22 +1,20 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- def build(bld): - common = bld.create_obj('cpp', 'shlib') - common.name = 'ns3-common' - common.target = common.name - common.uselib_local = ['ns3-core', 'ns3-simulator'] + common = bld.create_ns3_module('common', ['core', 'simulator']) common.source = [ 'buffer.cc', - 'chunk.cc', - 'header.cc', - 'trailer.cc', + 'chunk-registry.cc', 'packet-printer.cc', 'packet-metadata.cc', + 'packet-metadata-test.cc', 'packet.cc', 'tags.cc', + 'tag-registry.cc', 'pcap-writer.cc', 'variable-tracer-test.cc', 'trace-context.cc', + 'trace-context-element.cc', 'trace-resolver.cc', 'callback-trace-source.cc', 'empty-trace-resolver.cc', @@ -28,10 +26,12 @@ def build(bld): headers = bld.create_obj('ns3header') headers.source = [ 'buffer.h', - 'chunk.h', + 'chunk-registry.h', 'header.h', 'trailer.h', 'tags.h', + 'tag-registry.h', + 'tag.h', 'packet.h', 'packet-printer.h', 'packet-metadata.h', @@ -41,6 +41,7 @@ def build(bld): 'pcap-writer.h', 'callback-trace-source.h', 'trace-context.h', + 'trace-context-element.h', 'trace-resolver.h', 'empty-trace-resolver.h', 'composite-trace-resolver.h', diff --git a/src/core/assert.h b/src/core/assert.h index 928a3b76a..8dc935cdb 100644 --- a/src/core/assert.h +++ b/src/core/assert.h @@ -21,6 +21,12 @@ #ifndef ASSERT_H #define ASSERT_H +#ifdef NS3_ASSERT_ENABLE + +#include + +#include "breakpoint.h" + /** * \defgroup assert Assert * \brief assert functions and macros @@ -32,25 +38,6 @@ * removed in optimized builds. */ -namespace ns3 { - -/** - * \ingroup debugging - * - * When an NS_ASSERT cannot verify its condition, - * this function is called. This is where you should - * be able to put a breakpoint with a debugger if - * you want to catch assertions before the program - * halts. - */ -void AssertBreakpoint (void); - -}//namespace ns3 - -#ifdef NS3_ASSERT_ENABLE - -#include - /** * \ingroup assert * \param condition condition to verifiy. @@ -65,10 +52,10 @@ void AssertBreakpoint (void); { \ if (!(condition)) \ { \ - std::cout << "assert failed. file=" << __FILE__ << \ + std::cerr << "assert failed. file=" << __FILE__ << \ ", line=" << __LINE__ << ", cond=\""#condition << \ "\"" << std::endl; \ - ns3::AssertBreakpoint (); \ + NS_BREAKPOINT (); \ } \ } \ while (false) @@ -88,8 +75,8 @@ void AssertBreakpoint (void); { \ if (!(condition)) \ { \ - std::cout << message << std::endl; \ - ns3::AssertBreakpoint (); \ + std::cerr << message << std::endl; \ + NS_BREAKPOINT (); \ } \ } \ while (false) diff --git a/src/core/assert.cc b/src/core/breakpoint.cc similarity index 74% rename from src/core/assert.cc rename to src/core/breakpoint.cc index c780a37ea..35eab171e 100644 --- a/src/core/assert.cc +++ b/src/core/breakpoint.cc @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2006 INRIA + * Copyright (c) 2006,2007 INRIA, INESC Porto * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -17,14 +17,29 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mathieu Lacage + * Author: Gustavo Carneiro */ -#include "assert.h" +#include "breakpoint.h" +#include "ns3/core-config.h" +#ifdef HAVE_SIGNAL_H +# include +#endif namespace ns3 { +#if defined (HAVE_SIGNAL_H) && defined (SIGTRAP) + void -AssertBreakpoint (void) +BreakpointFallback (void) +{ + raise (SIGTRAP); +} + +#else + +void +BreakpointFallback (void) { int *a = 0; /** @@ -38,4 +53,6 @@ AssertBreakpoint (void) } } +#endif // HAVE_SIGNAL_H + }//namespace ns3 diff --git a/src/core/breakpoint.h b/src/core/breakpoint.h new file mode 100644 index 000000000..c2f541ad8 --- /dev/null +++ b/src/core/breakpoint.h @@ -0,0 +1,76 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006,2007 INESC Porto, INRIA + * All rights reserved. + * + * 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: Gustavo Carneiro + * Author: Mathieu Lacage + */ +#ifndef BREAKPOINT_H +#define BREAKPOINT_H + +namespace ns3 { + +/* Hacker macro to place breakpoints for selected machines. + * Actual use is strongly discouraged of course ;) + * Copied from GLib 2.12.9. + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/** + * \ingroup debugging + * + * Inserts a breakpoint instruction (or equivalent system call) into + * the code for selected machines. When an NS_ASSERT cannot verify its condition, + * this macro is used. Falls back to calling + * AssertBreakpoint() for architectures where breakpoint assembly + * instructions are not supported. + */ +#if (defined (__i386__) || defined (__amd64__) || defined (__x86_64__)) && defined (__GNUC__) && __GNUC__ >= 2 +# define NS_BREAKPOINT() \ + do{ __asm__ __volatile__ ("int $03"); }while(false) +#elif defined (_MSC_VER) && defined (_M_IX86) +# define NS_BREAKPOINT() \ + do{ __asm int 3h }while(false) +#elif defined (__alpha__) && !defined(__osf__) && defined (__GNUC__) && __GNUC__ >= 2 +# define NS_BREAKPOINT() \ + do{ __asm__ __volatile__ ("bpt"); }while(false) +#else /* !__i386__ && !__alpha__ */ +# define NS_BREAKPOINT() ns3::BreakpointFallback () +#endif + +/** + * \brief fallback breakpoint function + * + * This function is used by the NS_BREAKPOINT() macro as a fallback + * for when breakpoint assembly instructions are not available. It + * attempts to halt program execution either by a raising SIGTRAP, on + * unix systems, or by dereferencing a null pointer. + * + * Normally you should not call this function directly. + */ +void BreakpointFallback (void); + + +}//namespace ns3 + + +#endif /* BREAKPOINT_H */ diff --git a/src/core/callback.h b/src/core/callback.h index 79597cca0..94e58ca92 100644 --- a/src/core/callback.h +++ b/src/core/callback.h @@ -358,7 +358,7 @@ private: /** * \ingroup MakeCallback - * \param mem_ptr class method member pointer + * \param memPtr class method member pointer * \param objPtr class instance * \return a wrapper Callback * Build Callbacks for class method members which takes no arguments diff --git a/src/core/component-manager.h b/src/core/component-manager.h index ccecc4b15..fc2929eac 100644 --- a/src/core/component-manager.h +++ b/src/core/component-manager.h @@ -348,7 +348,7 @@ public: * result. */ template - static Ptr Create (ClassId classId, InterfaceId iid, T1 a1, T2 a2, T3 a3, T4 a4, T5); + static Ptr Create (ClassId classId, InterfaceId iid, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); private: friend void RegisterCallback (ClassId classId, CallbackBase *callback, diff --git a/src/core/debug.cc b/src/core/debug.cc index abd91b504..3dfb998b5 100644 --- a/src/core/debug.cc +++ b/src/core/debug.cc @@ -24,6 +24,7 @@ #include "debug.h" #include "assert.h" #include "ns3/core-config.h" +#include "fatal-error.h" #ifdef HAVE_STDLIB_H #include @@ -53,6 +54,7 @@ DebugComponentEnableEnvVar (void) { return; } + bool allFound = true; std::string env = envVar; std::string::size_type cur = 0; std::string::size_type next = 0; @@ -88,7 +90,7 @@ DebugComponentEnableEnvVar (void) } if (!found) { - std::cout << "No debug component named=\"" << tmp << "\"" << std::endl; + allFound = false; } if (next == std::string::npos) { @@ -100,6 +102,11 @@ DebugComponentEnableEnvVar (void) break; } } + if (allFound) + { + g_firstDebug = true; + } + #endif } @@ -122,7 +129,6 @@ DebugComponent::IsEnabled (void) if (g_firstDebug) { DebugComponentEnableEnvVar (); - g_firstDebug = false; } return m_isEnabled; } diff --git a/src/core/debug.h b/src/core/debug.h index 0171387f2..3fc6fef4b 100644 --- a/src/core/debug.h +++ b/src/core/debug.h @@ -110,7 +110,7 @@ private: { \ if (g_debug.IsEnabled ()) \ { \ - std::cout << msg << std::endl; \ + std::cerr << msg << std::endl; \ } \ } \ while (false) @@ -125,7 +125,7 @@ private: #define NS_DEBUG_UNCOND(msg) \ do \ { \ - std::cout << msg << std::endl; \ + std::cerr << msg << std::endl; \ } \ while (false) diff --git a/src/core/default-value.cc b/src/core/default-value.cc index 9fad9b6b2..4dcfcacbc 100644 --- a/src/core/default-value.cc +++ b/src/core/default-value.cc @@ -23,6 +23,52 @@ namespace ns3 { +namespace DefaultValue { + +enum BindStatus { + OK, + INVALID_VALUE, + NOT_FOUND +}; + + +static +enum BindStatus +BindSafe (std::string name, std::string value) +{ + for (DefaultValueList::Iterator i = DefaultValueList::Begin (); + i != DefaultValueList::End (); i++) + { + DefaultValueBase *cur = *i; + if (cur->GetName () == name) + { + if (!cur->ParseValue (value)) + { + return INVALID_VALUE; + } + return OK; + } + } + return NOT_FOUND; +} + +void +Bind (std::string name, std::string value) +{ + switch (BindSafe (name, value)) { + case INVALID_VALUE: + NS_FATAL_ERROR ("Invalid value: "<GetName () == name) - { - if (!cur->ParseValue (value)) - { - return INVALID_VALUE; - } - return OK; - } - } - return NOT_FOUND; -} - -void -Bind (std::string name, std::string value) -{ - switch (BindSafe (name, value)) { - case INVALID_VALUE: - NS_FATAL_ERROR ("Invalid value: "< i ("test-i", "help-i", -1); - if (i.GetValue () != -1) - { - ok = false; - } - Bind ("test-i", "-2"); - if (i.GetValue () != -2) - { - ok = false; - } - Bind ("test-i", "+2"); - if (i.GetValue () != 2) - { - ok = false; - } - if (i.GetType () != "int32_t(-2147483648:2147483647)") - { - ok = false; - } + NumericDefaultValue i ("test-i", "help-i", -1); + NS_TEST_ASSERT_EQUAL (i.GetValue (), -1); + DefaultValue::Bind ("test-i", "-2"); + NS_TEST_ASSERT_EQUAL (i.GetValue (), -2); + DefaultValue::Bind ("test-i", "+2"); + NS_TEST_ASSERT_EQUAL (i.GetValue (), 2); + NS_TEST_ASSERT_EQUAL (i.GetType (), "int32_t(-2147483648:2147483647)"); NumericDefaultValue ui32 ("test-ui32", "help-ui32", 10); - if (ui32.GetType () != "uint32_t(0:4294967295)") - { - ok = false; - } - NumericDefaultValue c ("test-c", "help-c", 10); - if (c.GetValue () != 10) - { - ok = false; - } - Bind ("test-c", "257"); + NS_TEST_ASSERT_EQUAL (ui32.GetType (), "uint32_t(0:4294967295)"); + NumericDefaultValue c ("test-c", "help-c", 10); + NS_TEST_ASSERT_EQUAL (c.GetValue (), 10); + DefaultValue::Bind ("test-c", "257"); NumericDefaultValue x ("test-x", "help-x", 10.0); NumericDefaultValue y ("test-y", "help-y", 10.0); @@ -429,19 +388,10 @@ DefaultValueTest::RunTests (void) MY_ENUM_A, "A", MY_ENUM_B, "B", 0, (void*)0); - if (e.GetValue () != MY_ENUM_C) - { - ok = false; - } - Bind ("test-e", "B"); - if (e.GetValue () != MY_ENUM_B) - { - ok = false; - } - if (BindSafe ("test-e", "D") != INVALID_VALUE) - { - ok = false; - } + NS_TEST_ASSERT_EQUAL (e.GetValue (), MY_ENUM_C); + DefaultValue::Bind ("test-e", "B"); + NS_TEST_ASSERT_EQUAL (e.GetValue (), MY_ENUM_B); + NS_TEST_ASSERT_EQUAL (DefaultValue::BindSafe ("test-e", "D"), DefaultValue::INVALID_VALUE); class MyEnumSubclass : public EnumDefaultValue { @@ -456,15 +406,9 @@ DefaultValueTest::RunTests (void) AddPossibleValue (MY_ENUM_D, "D"); } } e1 ; - if (e1.GetValue () != MY_ENUM_B) - { - ok = false; - } - Bind ("test-e1", "D"); - if (e1.GetValue () != MY_ENUM_D) - { - ok = false; - } + NS_TEST_ASSERT_EQUAL (e1.GetValue (), MY_ENUM_B); + DefaultValue::Bind ("test-e1", "D"); + NS_TEST_ASSERT_EQUAL (e1.GetValue (), MY_ENUM_D); DefaultValueList::Remove ("test-e1"); DefaultValueList::Remove ("test-e"); @@ -474,7 +418,7 @@ DefaultValueTest::RunTests (void) DefaultValueList::Remove ("test-c"); DefaultValueList::Remove ("test-ui32"); - return ok; + return result; } static DefaultValueTest g_default_value_tests; diff --git a/src/core/default-value.h b/src/core/default-value.h index f57b3b4e0..b43f013be 100644 --- a/src/core/default-value.h +++ b/src/core/default-value.h @@ -31,6 +31,25 @@ namespace ns3 { +namespace DefaultValue +{ + +/** + * \ingroup config + * \param name name of variable to bind + * \param value value to bind to the specified variable + * + * If the variable name does not match any existing + * variable or if the value is not compatible with + * the variable type, this function will abort + * at runtime and print an error message detailing + * which variable or value triggered the problem. + */ +void Bind (std::string name, std::string value); + +} + + class DefaultValueBase { public: @@ -83,19 +102,6 @@ class DefaultValueList static List *GetList (void); }; -/** - * \ingroup config - * \param name name of variable to bind - * \param value value to bind to the specified variable - * - * If the variable name does not match any existing - * variable or if the value is not compatible with - * the variable type, this function will abort - * at runtime and print an error message detailing - * which variable or value triggered the problem. - */ -void Bind (std::string name, std::string value); - /** * \brief A Boolean variable for ns3::Bind * \ingroup config diff --git a/src/core/fatal-error.h b/src/core/fatal-error.h index bcb22269b..2c83de580 100644 --- a/src/core/fatal-error.h +++ b/src/core/fatal-error.h @@ -21,7 +21,7 @@ #ifndef FATAL_ERROR_H #define FATAL_ERROR_H -#include "assert.h" +#include "breakpoint.h" #include /** @@ -32,15 +32,15 @@ * * When this macro is hit at runtime, the user-specified * error message is output and the program is halted by calling - * the ns3::AssertBreakpoint function. This macro is enabled + * the NS_DEBUG_BREAKPOINT macro. This macro is enabled * unconditionally in all builds, including debug and optimized * builds. */ #define NS_FATAL_ERROR(msg) \ do \ { \ - std::cout << msg << std::endl; \ - ns3::AssertBreakpoint (); \ + std::cerr << msg << std::endl; \ + NS_BREAKPOINT (); \ } \ while (false) diff --git a/src/core/ptr.cc b/src/core/ptr.cc index 10e63c606..cee3f59e2 100644 --- a/src/core/ptr.cc +++ b/src/core/ptr.cc @@ -28,9 +28,14 @@ namespace ns3 { +template +void Foo (void) {} + + class NoCount : public Object { public: + NoCount (void (*fn) (void)); NoCount (Callback cb); ~NoCount (); void Nothing (void) const; @@ -292,12 +297,22 @@ PtrTest::RunTests (void) callback (); } + #if 0 // as expected, fails compilation. { Ptr p = Create (cb); Callback callback = MakeCallback (&NoCount::Nothing, p); } + // local types are not allowed as arguments to a template. + { + class B + { + public: + B () {} + }; + Foo (); + } #endif diff --git a/src/core/ptr.h b/src/core/ptr.h index f5a52292e..f2436dd63 100644 --- a/src/core/ptr.h +++ b/src/core/ptr.h @@ -65,7 +65,7 @@ private: template friend U *PeekPointer (const Ptr &p); - void Acquire (void) const; + inline void Acquire (void) const; public: /** * Create an empty smart pointer @@ -81,7 +81,16 @@ public: * same, so that object is deleted if no more references to it * remain. */ - Ptr (T *ptr); + Ptr (T *ptr); + /** + * \param ptr raw pointer to manage + * \param ref if set to true, this method calls Ref, otherwise, + * it does not call Ref. + * + * Create a smart pointer which points to the object pointed to by + * the input raw pointer ptr. + */ + Ptr (T *ptr, bool ref); Ptr (Ptr const&o); // allow conversions from T to T const. template @@ -378,6 +387,16 @@ Ptr::Ptr (T *ptr) Acquire (); } +template +Ptr::Ptr (T *ptr, bool ref) + : m_ptr (ptr) +{ + if (ref) + { + Acquire (); + } +} + template Ptr::Ptr (Ptr const&o) : m_ptr (PeekPointer (o)) diff --git a/src/core/random-variable.cc b/src/core/random-variable.cc index f93404fda..fa44440d0 100644 --- a/src/core/random-variable.cc +++ b/src/core/random-variable.cc @@ -108,8 +108,8 @@ void RandomVariable::UseGlobalSeed(uint32_t s0, uint32_t s1, uint32_t s2, { if (RandomVariable::globalSeedSet) { - cout << "Random number generator already initialized!" << endl; - cout << "Call to RandomVariable::UseGlobalSeed() ignored" << endl; + cerr << "Random number generator already initialized!" << endl; + cerr << "Call to RandomVariable::UseGlobalSeed() ignored" << endl; return; } RandomVariable::globalSeed[0] = s0; @@ -537,7 +537,7 @@ void EmpiricalVariable::Validate() ValueCDF& current = emp[i]; if (current.value < prior.value || current.cdf < prior.cdf) { // Error - cout << "Empirical Dist error," + cerr << "Empirical Dist error," << " current value " << current.value << " prior value " << prior.value << " current cdf " << current.cdf diff --git a/src/core/random-variable.h b/src/core/random-variable.h index 7759ea157..ce4cebd4d 100644 --- a/src/core/random-variable.h +++ b/src/core/random-variable.h @@ -772,6 +772,7 @@ public: /** * \param s Low end of the range * \param l High end of the range + * \param mean mean of the distribution * \return A triangularly distributed random number between s and l */ static double GetSingleValue(double s, double l, double mean); diff --git a/src/core/rng-stream.cc b/src/core/rng-stream.cc index cca0e9c4d..e24ab052c 100644 --- a/src/core/rng-stream.cc +++ b/src/core/rng-stream.cc @@ -269,11 +269,11 @@ bool RngStream::CheckSeed (const uint32_t seed[6]) } for (i = 3; i < 6; ++i) { if (seed[i] >= m2) { - cout << "Seed[" << i << "] = " << seed[i] << endl; - cerr << "*****************************************\n\n" - << "ERROR: Seed[" << i << "] >= 4294944443, Seed is not set." - << "\n\n*****************************************\n\n"; - return (false); + cerr << "Seed[" << i << "] = " << seed[i] << endl; + cerr << "*****************************************\n\n" + << "ERROR: Seed[" << i << "] >= 4294944443, Seed is not set." + << "\n\n*****************************************\n\n"; + return (false); } } if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0) { diff --git a/src/core/uid-manager.cc b/src/core/uid-manager.cc index f9af50622..f6aa3f6de 100644 --- a/src/core/uid-manager.cc +++ b/src/core/uid-manager.cc @@ -20,6 +20,7 @@ */ #include "uid-manager.h" #include "ns3/fatal-error.h" +#include "ns3/assert.h" namespace ns3 { diff --git a/src/core/wscript b/src/core/wscript index 219f5541f..bba53ae71 100644 --- a/src/core/wscript +++ b/src/core/wscript @@ -15,18 +15,22 @@ def configure(conf): e.define = 'HAVE_GETENV' e.run() + e = conf.create_header_configurator() + e.mandatory = False + e.name = 'signal.h' + e.define = 'HAVE_SIGNAL_H' + e.run() + conf.write_config_header('ns3/core-config.h') def build(bld): - core = bld.create_obj('cpp', 'shlib') - core.name = 'ns3-core' - core.target = core.name + core = bld.create_ns3_module('core') core.source = [ 'callback-test.cc', 'debug.cc', - 'assert.cc', + 'breakpoint.cc', 'ptr.cc', 'object.cc', 'test.cc', @@ -58,6 +62,7 @@ def build(bld): 'object.h', 'debug.h', 'assert.h', + 'breakpoint.h', 'fatal-error.h', 'test.h', 'random-variable.h', diff --git a/src/devices/csma-cd/wscript b/src/devices/csma-cd/wscript deleted file mode 100644 index 31497fd51..000000000 --- a/src/devices/csma-cd/wscript +++ /dev/null @@ -1,24 +0,0 @@ -## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- - - -def build(bld): - obj = bld.create_obj('cpp', 'shlib') - obj.name = 'ns3-csma-cd' - obj.target = obj.name - obj.uselib_local = ['ns3-node'] - obj.source = [ - 'backoff.cc', - 'csma-cd-net-device.cc', - 'csma-cd-channel.cc', - 'csma-cd-topology.cc', - 'csma-cd-ipv4-topology.cc', - ] - headers = bld.create_obj('ns3header') - headers.source = [ - 'backoff.h', - 'csma-cd-net-device.h', - 'csma-cd-channel.h', - 'csma-cd-topology.h', - 'csma-cd-ipv4-topology.h', - ] - diff --git a/src/devices/csma-cd/backoff.cc b/src/devices/csma/backoff.cc similarity index 100% rename from src/devices/csma-cd/backoff.cc rename to src/devices/csma/backoff.cc diff --git a/src/devices/csma-cd/backoff.h b/src/devices/csma/backoff.h similarity index 100% rename from src/devices/csma-cd/backoff.h rename to src/devices/csma/backoff.h diff --git a/src/devices/csma-cd/csma-cd-channel.cc b/src/devices/csma/csma-channel.cc similarity index 63% rename from src/devices/csma-cd/csma-cd-channel.cc rename to src/devices/csma/csma-channel.cc index 7b6d211f7..9ba42d854 100644 --- a/src/devices/csma-cd/csma-cd-channel.cc +++ b/src/devices/csma/csma-channel.cc @@ -19,60 +19,60 @@ * Author: Emmanuelle Laprise */ -#include "csma-cd-channel.h" -#include "csma-cd-net-device.h" +#include "csma-channel.h" +#include "csma-net-device.h" #include "ns3/packet.h" #include "ns3/simulator.h" #include "ns3/debug.h" -NS_DEBUG_COMPONENT_DEFINE ("CsmaCdChannel"); +NS_DEBUG_COMPONENT_DEFINE ("CsmaChannel"); namespace ns3 { -CsmaCdDeviceRec::CsmaCdDeviceRec() +CsmaDeviceRec::CsmaDeviceRec() { active = false; } -CsmaCdDeviceRec::CsmaCdDeviceRec(Ptr device) +CsmaDeviceRec::CsmaDeviceRec(Ptr device) { devicePtr = device; active = true; } bool -CsmaCdDeviceRec::IsActive() { +CsmaDeviceRec::IsActive() { return active; } // -// By default, you get a channel with the name "CsmaCd Channel" that +// By default, you get a channel with the name "Csma Channel" that // has an "infitely" fast transmission speed and zero delay. -CsmaCdChannel::CsmaCdChannel() +CsmaChannel::CsmaChannel() : - Channel ("CsmaCd Channel"), + Channel ("Csma Channel"), m_bps (DataRate(0xffffffff)), m_delay (Seconds(0)) { - NS_DEBUG("CsmaCdChannel::CsmaCdChannel ()"); + NS_DEBUG("CsmaChannel::CsmaChannel ()"); Init(); } -CsmaCdChannel::CsmaCdChannel( +CsmaChannel::CsmaChannel( const DataRate& bps, const Time& delay) : - Channel ("CsmaCd Channel"), + Channel ("Csma Channel"), m_bps (bps), m_delay (delay) { - NS_DEBUG("CsmaCdChannel::CsmaCdChannel (" << Channel::GetName() + NS_DEBUG("CsmaChannel::CsmaChannel (" << Channel::GetName() << ", " << bps.GetBitRate() << ", " << delay << ")"); Init(); } -CsmaCdChannel::CsmaCdChannel( +CsmaChannel::CsmaChannel( const std::string& name, const DataRate& bps, const Time& delay) @@ -81,35 +81,35 @@ CsmaCdChannel::CsmaCdChannel( m_bps (bps), m_delay (delay) { - NS_DEBUG("CsmaCdChannel::CsmaCdChannel (" << name << ", " << + NS_DEBUG("CsmaChannel::CsmaChannel (" << name << ", " << bps.GetBitRate() << ", " << delay << ")"); Init(); } -void CsmaCdChannel::Init() { +void CsmaChannel::Init() { m_state = IDLE; m_deviceList.clear(); } int32_t -CsmaCdChannel::Attach(Ptr device) +CsmaChannel::Attach(Ptr device) { - NS_DEBUG("CsmaCdChannel::Attach (" << device << ")"); + NS_DEBUG("CsmaChannel::Attach (" << device << ")"); NS_ASSERT(device != 0); - CsmaCdDeviceRec rec(device); + CsmaDeviceRec rec(device); m_deviceList.push_back(rec); return (m_deviceList.size() - 1); } bool -CsmaCdChannel::Reattach(Ptr device) +CsmaChannel::Reattach(Ptr device) { - NS_DEBUG("CsmaCdChannel::Reattach (" << device << ")"); + NS_DEBUG("CsmaChannel::Reattach (" << device << ")"); NS_ASSERT(device != 0); - std::vector::iterator it; + std::vector::iterator it; for (it = m_deviceList.begin(); it < m_deviceList.end(); it++) { if (it->devicePtr == device) @@ -129,9 +129,9 @@ CsmaCdChannel::Reattach(Ptr device) } bool -CsmaCdChannel::Reattach(uint32_t deviceId) +CsmaChannel::Reattach(uint32_t deviceId) { - NS_DEBUG("CsmaCdChannel::Reattach (" << deviceId << ")"); + NS_DEBUG("CsmaChannel::Reattach (" << deviceId << ")"); if (deviceId < m_deviceList.size()) { return false; @@ -149,15 +149,15 @@ CsmaCdChannel::Reattach(uint32_t deviceId) } bool -CsmaCdChannel::Detach(uint32_t deviceId) +CsmaChannel::Detach(uint32_t deviceId) { - NS_DEBUG("CsmaCdChannel::Detach (" << deviceId << ")"); + NS_DEBUG("CsmaChannel::Detach (" << deviceId << ")"); if (deviceId < m_deviceList.size()) { if (!m_deviceList[deviceId].active) { - NS_DEBUG("CsmaCdChannel::Detach Device is already detached (" + NS_DEBUG("CsmaChannel::Detach Device is already detached (" << deviceId << ")"); return false; } @@ -165,7 +165,7 @@ CsmaCdChannel::Detach(uint32_t deviceId) m_deviceList[deviceId].active = false; if ((m_state == TRANSMITTING) && (m_currentSrc == deviceId)) { - NS_DEBUG("CsmaCdChannel::Detach Device is currently" + NS_DEBUG("CsmaChannel::Detach Device is currently" << "transmitting (" << deviceId << ")"); // Here we will need to place a warning in the packet } @@ -179,12 +179,12 @@ CsmaCdChannel::Detach(uint32_t deviceId) } bool -CsmaCdChannel::Detach(Ptr device) +CsmaChannel::Detach(Ptr device) { - NS_DEBUG("CsmaCdChannel::Detach (" << device << ")"); + NS_DEBUG("CsmaChannel::Detach (" << device << ")"); NS_ASSERT(device != 0); - std::vector::iterator it; + std::vector::iterator it; for (it = m_deviceList.begin(); it < m_deviceList.end(); it++) { if ((it->devicePtr == device) && (it->active)) @@ -197,27 +197,27 @@ CsmaCdChannel::Detach(Ptr device) } bool -CsmaCdChannel::TransmitStart(Packet& p, uint32_t srcId) +CsmaChannel::TransmitStart(Packet& p, uint32_t srcId) { - NS_DEBUG ("CsmaCdChannel::TransmitStart (" << &p << ", " << srcId + NS_DEBUG ("CsmaChannel::TransmitStart (" << &p << ", " << srcId << ")"); - NS_DEBUG ("CsmaCdChannel::TransmitStart (): UID is " << + NS_DEBUG ("CsmaChannel::TransmitStart (): UID is " << p.GetUid () << ")"); if (m_state != IDLE) { - NS_DEBUG("CsmaCdChannel::TransmitStart (): state is not IDLE"); + NS_DEBUG("CsmaChannel::TransmitStart (): state is not IDLE"); return false; } if (!IsActive(srcId)) { - NS_DEBUG("CsmaCdChannel::TransmitStart (): ERROR: Seclected " + NS_DEBUG("CsmaChannel::TransmitStart (): ERROR: Seclected " << "source is not currently attached to network"); return false; } - NS_DEBUG("CsmaCdChannel::TransmitStart (): switch to TRANSMITTING"); + NS_DEBUG("CsmaChannel::TransmitStart (): switch to TRANSMITTING"); m_currentPkt = p; m_currentSrc = srcId; m_state = TRANSMITTING; @@ -225,17 +225,17 @@ CsmaCdChannel::TransmitStart(Packet& p, uint32_t srcId) } bool -CsmaCdChannel::IsActive(uint32_t deviceId) +CsmaChannel::IsActive(uint32_t deviceId) { return (m_deviceList[deviceId].active); } bool -CsmaCdChannel::TransmitEnd() +CsmaChannel::TransmitEnd() { - NS_DEBUG("CsmaCdChannel::TransmitEnd (" << &m_currentPkt << ", " + NS_DEBUG("CsmaChannel::TransmitEnd (" << &m_currentPkt << ", " << m_currentSrc << ")"); - NS_DEBUG("CsmaCdChannel::TransmitEnd (): UID is " << + NS_DEBUG("CsmaChannel::TransmitEnd (): UID is " << m_currentPkt.GetUid () << ")"); NS_ASSERT(m_state == TRANSMITTING); @@ -244,34 +244,33 @@ CsmaCdChannel::TransmitEnd() bool retVal = true; if (!IsActive(m_currentSrc)) { - NS_DEBUG("CsmaCdChannel::TransmitEnd (): ERROR: Seclected source " + NS_DEBUG("CsmaChannel::TransmitEnd (): ERROR: Seclected source " << "was detached before the end of the transmission"); retVal = false; } - NS_DEBUG ("CsmaCdChannel::TransmitEnd (): Schedule event in " << + NS_DEBUG ("CsmaChannel::TransmitEnd (): Schedule event in " << m_delay.GetSeconds () << "sec"); Simulator::Schedule (m_delay, - &CsmaCdChannel::PropagationCompleteEvent, + &CsmaChannel::PropagationCompleteEvent, this); return retVal; } void -CsmaCdChannel::PropagationCompleteEvent() +CsmaChannel::PropagationCompleteEvent() { - NS_DEBUG("CsmaCdChannel::PropagationCompleteEvent (" + NS_DEBUG("CsmaChannel::PropagationCompleteEvent (" << &m_currentPkt << ")"); - NS_DEBUG ("CsmaCdChannel::PropagationCompleteEvent (): UID is " << + NS_DEBUG ("CsmaChannel::PropagationCompleteEvent (): UID is " << m_currentPkt.GetUid () << ")"); NS_ASSERT(m_state == PROPAGATING); - m_state = IDLE; - NS_DEBUG ("CsmaCdChannel::PropagationCompleteEvent (): Receive"); + NS_DEBUG ("CsmaChannel::PropagationCompleteEvent (): Receive"); - std::vector::iterator it; + std::vector::iterator it; for (it = m_deviceList.begin(); it < m_deviceList.end(); it++) { if (it->IsActive()) @@ -279,14 +278,15 @@ CsmaCdChannel::PropagationCompleteEvent() it->devicePtr->Receive (m_currentPkt); } } + m_state = IDLE; } uint32_t -CsmaCdChannel::GetNumActDevices (void) +CsmaChannel::GetNumActDevices (void) { int numActDevices = 0; - std::vector::iterator it; + std::vector::iterator it; for (it = m_deviceList.begin(); it < m_deviceList.end(); it++) { if (it->active) @@ -300,24 +300,24 @@ CsmaCdChannel::GetNumActDevices (void) // This is not the number of active devices. This is the total number // of devices even if some were detached after. uint32_t -CsmaCdChannel::GetNDevices (void) const +CsmaChannel::GetNDevices (void) const { return (m_deviceList.size()); } Ptr -CsmaCdChannel::GetDevice (uint32_t i) const +CsmaChannel::GetDevice (uint32_t i) const { - Ptr< CsmaCdNetDevice > netDevice; + Ptr< CsmaNetDevice > netDevice; netDevice = m_deviceList[i].devicePtr; return netDevice; } int32_t -CsmaCdChannel::GetDeviceNum (Ptr device) +CsmaChannel::GetDeviceNum (Ptr device) { - std::vector::iterator it; + std::vector::iterator it; int i = 0; for (it = m_deviceList.begin(); it < m_deviceList.end(); it++) { @@ -338,7 +338,7 @@ CsmaCdChannel::GetDeviceNum (Ptr device) } bool -CsmaCdChannel::IsBusy (void) +CsmaChannel::IsBusy (void) { if (m_state == IDLE) { @@ -351,19 +351,19 @@ CsmaCdChannel::IsBusy (void) } DataRate -CsmaCdChannel::GetDataRate (void) +CsmaChannel::GetDataRate (void) { return m_bps; } Time -CsmaCdChannel::GetDelay (void) +CsmaChannel::GetDelay (void) { return m_delay; } WireState -CsmaCdChannel::GetState(void) +CsmaChannel::GetState(void) { return m_state; } diff --git a/src/devices/csma-cd/csma-cd-channel.h b/src/devices/csma/csma-channel.h similarity index 90% rename from src/devices/csma-cd/csma-cd-channel.h rename to src/devices/csma/csma-channel.h index fafb5b6ad..0efabf77e 100644 --- a/src/devices/csma-cd/csma-cd-channel.h +++ b/src/devices/csma/csma-channel.h @@ -18,8 +18,8 @@ * Author: Emmanuelle Laprise */ -#ifndef CSMA_CD_CHANNEL_H -#define CSMA_CD_CHANNEL_H +#ifndef CSMA_CHANNEL_H +#define CSMA_CHANNEL_H #include "ns3/channel.h" #include "ns3/ptr.h" @@ -29,21 +29,21 @@ namespace ns3 { -class CsmaCdNetDevice; +class CsmaNetDevice; /** - * \brief CsmaCdNetDevice Record + * \brief CsmaNetDevice Record * * Stores the information related to each net device that is * connected to the channel. */ - class CsmaCdDeviceRec { + class CsmaDeviceRec { public: - Ptr< CsmaCdNetDevice > devicePtr; /// Pointer to the net device + Ptr< CsmaNetDevice > devicePtr; /// Pointer to the net device bool active; /// Is net device enabled to TX/RX - CsmaCdDeviceRec(); - CsmaCdDeviceRec(Ptr< CsmaCdNetDevice > device); + CsmaDeviceRec(); + CsmaDeviceRec(Ptr< CsmaNetDevice > device); /* * \return If the net device pointed to by the devicePtr is active * and ready to RX/TX. @@ -65,9 +65,9 @@ class CsmaCdNetDevice; }; /** - * \brief CsmaCd Channel. + * \brief Csma Channel. * - * This class represents a simple Csma/Cd channel that can be used + * This class represents a simple Csma channel that can be used * 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 @@ -84,32 +84,32 @@ class CsmaCdNetDevice; * packet to the channel is really connected to this channel * */ -class CsmaCdChannel : public Channel { +class CsmaChannel : public Channel { public: /** - * \brief Create a CsmaCdChannel + * \brief Create a CsmaChannel * - * By default, you get a channel with the name "CsmaCd Channel" that + * By default, you get a channel with the name "Csma Channel" that * has an "infitely" fast transmission speed and zero delay. */ - CsmaCdChannel (); + CsmaChannel (); /** - * \brief Create a CsmaCdChannel + * \brief Create a CsmaChannel * * \param bps The bitrate of the channel * \param delay Transmission delay through the channel */ - CsmaCdChannel (const DataRate& bps, const Time& delay); + CsmaChannel (const DataRate& bps, const Time& delay); /** - * \brief Create a CsmaCdChannel + * \brief Create a CsmaChannel * * \param name the name of the channel for identification purposes * \param bps The bitrate of the channel * \param delay Transmission delay through the channel */ - CsmaCdChannel (const std::string& name, + CsmaChannel (const std::string& name, const DataRate& bps, const Time& delay); /** @@ -118,7 +118,7 @@ public: * \param device Device pointer to the netdevice to attach to the channel * \return The assigned device number */ - int32_t Attach (Ptr device); + int32_t Attach (Ptr device); /** * \brief Detach a given netdevice from this channel * @@ -130,7 +130,7 @@ public: * false if the device is not currently connected to the channel or * can't be found. */ - bool Detach (Ptr device); + bool Detach (Ptr device); /** * \brief Detach a given netdevice from this channel * @@ -170,7 +170,7 @@ public: * channel, false if the device is currently connected to the * channel or can't be found. */ - bool Reattach(Ptr device); + bool Reattach(Ptr device); /** * \brief Start transmitting a packet over the channel * @@ -216,7 +216,7 @@ public: * \param device Device pointer to the netdevice for which the device * number is needed */ - int32_t GetDeviceNum (Ptr device); + int32_t GetDeviceNum (Ptr device); /** * \return Returns the state of the channel (IDLE -- free, * TRANSMITTING -- busy, PROPAGATING - busy ) @@ -278,7 +278,7 @@ private: * whole list does not have to be searched when making sure that a * source is attached to a channel when it is transmitting data. */ - std::vector< CsmaCdDeviceRec > m_deviceList; + std::vector< CsmaDeviceRec > m_deviceList; /** * Packet that is currently being transmitted on the channel (or last * packet to have been transmitted on the channel if the channel is @@ -304,4 +304,4 @@ private: } // namespace ns3 -#endif /* CSMA_CD_CHANNEL_H */ +#endif /* CSMA_CHANNEL_H */ diff --git a/src/devices/csma-cd/csma-cd-ipv4-topology.cc b/src/devices/csma/csma-ipv4-topology.cc similarity index 70% rename from src/devices/csma-cd/csma-cd-ipv4-topology.cc rename to src/devices/csma/csma-ipv4-topology.cc index be04cc8d4..02cd0e487 100644 --- a/src/devices/csma-cd/csma-cd-ipv4-topology.cc +++ b/src/devices/csma/csma-ipv4-topology.cc @@ -28,23 +28,22 @@ #include "ns3/ipv4.h" #include "ns3/queue.h" -#include "csma-cd-channel.h" -#include "csma-cd-net-device.h" -#include "csma-cd-ipv4-topology.h" +#include "csma-channel.h" +#include "csma-net-device.h" +#include "csma-ipv4-topology.h" namespace ns3 { - uint32_t -CsmaCdIpv4Topology::AddIpv4CsmaCdNode(Ptr n1, - Ptr ch, - MacAddress addr) +CsmaIpv4Topology::AddIpv4CsmaNode(Ptr n1, + Ptr ch, + Eui48Address addr) { Ptr q = Queue::CreateDefault (); // assume full-duplex - Ptr nd0 = Create (n1, addr, - ns3::CsmaCdNetDevice::IP_ARP, + Ptr nd0 = Create (n1, addr, + ns3::CsmaNetDevice::IP_ARP, true, true); nd0->AddQueue(q); nd0->Attach (ch); @@ -53,47 +52,47 @@ CsmaCdIpv4Topology::AddIpv4CsmaCdNode(Ptr n1, void -CsmaCdIpv4Topology::AddIpv4LlcCsmaCdNode(Ptr n1, - Ptr ch, - MacAddress addr) +CsmaIpv4Topology::AddIpv4LlcCsmaNode(Ptr n1, + Ptr ch, + Eui48Address addr) { Ptr q = Queue::CreateDefault (); - Ptr nd0 = Create (n1, addr, - ns3::CsmaCdNetDevice::LLC, + Ptr nd0 = Create (n1, addr, + ns3::CsmaNetDevice::LLC, true, false); nd0->AddQueue(q); nd0->Attach (ch); - Ptr nd1 = Create (n1, addr, - ns3::CsmaCdNetDevice::LLC, + Ptr nd1 = Create (n1, addr, + ns3::CsmaNetDevice::LLC, false, true); nd1->AddQueue(q); nd1->Attach (ch); } void -CsmaCdIpv4Topology::AddIpv4RawCsmaCdNode(Ptr n1, - Ptr ch, - MacAddress addr) +CsmaIpv4Topology::AddIpv4RawCsmaNode(Ptr n1, + Ptr ch, + Eui48Address addr) { Ptr q = Queue::CreateDefault (); - Ptr nd0 = Create (n1, addr, - ns3::CsmaCdNetDevice::RAW, + Ptr nd0 = Create (n1, addr, + ns3::CsmaNetDevice::RAW, true, false); nd0->AddQueue(q); nd0->Attach (ch); - Ptr nd1 = Create (n1, addr, - ns3::CsmaCdNetDevice::RAW, + Ptr nd1 = Create (n1, addr, + ns3::CsmaNetDevice::RAW, false, true); nd1->AddQueue(q); nd1->Attach (ch); } void -CsmaCdIpv4Topology::AddIpv4Address(Ptr n1, +CsmaIpv4Topology::AddIpv4Address(Ptr n1, int ndNum, const Ipv4Address& addr1, const Ipv4Mask& netmask1) @@ -115,7 +114,7 @@ CsmaCdIpv4Topology::AddIpv4Address(Ptr n1, } void -CsmaCdIpv4Topology::AddIpv4Routes ( +CsmaIpv4Topology::AddIpv4Routes ( Ptr nd1, Ptr nd2) { // Assert that both are Ipv4 nodes diff --git a/src/devices/csma-cd/csma-cd-ipv4-topology.h b/src/devices/csma/csma-ipv4-topology.h similarity index 55% rename from src/devices/csma-cd/csma-cd-ipv4-topology.h rename to src/devices/csma/csma-ipv4-topology.h index d31ee4184..86be99d7a 100644 --- a/src/devices/csma-cd/csma-cd-ipv4-topology.h +++ b/src/devices/csma/csma-ipv4-topology.h @@ -18,22 +18,22 @@ // Author: Emmanuelle Laprise // -#ifndef __CSMA_CD_IPV4_TOPOLOGY_H__ -#define __CSMA_CD_IPV4_TOPOLOGY_H__ +#ifndef __CSMA_IPV4_TOPOLOGY_H__ +#define __CSMA_IPV4_TOPOLOGY_H__ #include "ns3/ptr.h" #include "ns3/ipv4-address.h" #include "ns3/ipv4.h" #include "ns3/ipv4-route.h" #include "ns3/internet-node.h" -#include "ns3/csma-cd-net-device.h" +#include "ns3/csma-net-device.h" // The topology class consists of only static methods thar are used to // create the topology and data flows for an ns3 simulation namespace ns3 { -class CsmaCdIpv4Channel; +class CsmaIpv4Channel; class Node; class IPAddr; class DataRate; @@ -41,55 +41,55 @@ class Queue; /** * \brief A helper class to create Topologies based on the - * InternetNodes and CsmaCdChannels. Either the - * SimpleCsmaCdNetDevice or the LLCCsmaCdNetDevice can be used + * InternetNodes and CsmaChannels. Either the + * SimpleCsmaNetDevice or the LLCCsmaNetDevice can be used * when constructing these topologies. */ -class CsmaCdIpv4Topology { +class CsmaIpv4Topology { public: /** - * \param n1 Node to be attached to the Csma/Cd channel - * \param ch CsmaCdChannel to which node n1 should be attached + * \param n1 Node to be attached to the Csma channel + * \param ch CsmaChannel to which node n1 should be attached * \param addr Mac address of the node * - * Add a Csma/Cd node to a Csma/Cd channel. This function adds - * a EthernetCsmaCdNetDevice to the nodes so that they can - * connect to a CsmaCdChannel. This means that Ethernet headers + * Add a Csma node to a Csma channel. This function adds + * a EthernetCsmaNetDevice to the nodes so that they can + * connect to a CsmaChannel. This means that Ethernet headers * and trailers will be added to the packet before sending out on * the net device. * * \return ifIndex of the device */ - static uint32_t AddIpv4CsmaCdNode( Ptr n1, - Ptr ch, - MacAddress addr); + static uint32_t AddIpv4CsmaNode( Ptr n1, + Ptr ch, + Eui48Address addr); /** - * \param n1 Node to be attached to the Csma/Cd channel - * \param ch CsmaCdChannel to which node n1 should be attached + * \param n1 Node to be attached to the Csma channel + * \param ch CsmaChannel to which node n1 should be attached * \param addr Mac address of the node * - * Add a Csma/Cd node to a Csma/Cd channel. This function adds - * a RawCsmaCdNetDevice to the nodes so that they can connect - * to a CsmaCdChannel. + * Add a Csma node to a Csma channel. This function adds + * a RawCsmaNetDevice to the nodes so that they can connect + * to a CsmaChannel. */ - static void AddIpv4RawCsmaCdNode( Ptr n1, - Ptr ch, - MacAddress addr); + static void AddIpv4RawCsmaNode( Ptr n1, + Ptr ch, + Eui48Address addr); /** - * \param n1 Node to be attached to the Csma/Cd channel - * \param ch CsmaCdChannel to which node n1 should be attached + * \param n1 Node to be attached to the Csma channel + * \param ch CsmaChannel to which node n1 should be attached * \param addr Mac address of the node * - * Add a Csma/Cd node to a Csma/Cd channel. This function adds - * a LlcCsmaCdNetDevice to the nodes so that they can connect - * to a CsmaCdChannel. + * Add a Csma node to a Csma channel. This function adds + * a LlcCsmaNetDevice to the nodes so that they can connect + * to a CsmaChannel. */ - static void AddIpv4LlcCsmaCdNode( Ptr n1, - Ptr ch, - MacAddress addr); + static void AddIpv4LlcCsmaNode( Ptr n1, + Ptr ch, + Eui48Address addr); @@ -97,11 +97,11 @@ public: * \param n1 Node * \param ndNum NetDevice number with which to associate address * \param addr1 Ipv4 Address for ndNum of n1 - * \param network network mask for ndNum of node n1 + * \param netmask1 network mask for ndNum of node n1 * * Add an Ipv4Address to the Ipv4 interface associated with the - * ndNum CsmaCdIpv4NetDevices on the provided - * CsmaCdIpv4Channel + * ndNum CsmaIpv4NetDevices on the provided + * CsmaIpv4Channel */ static void AddIpv4Address(Ptr n1, int ndNum, const Ipv4Address& addr1, diff --git a/src/devices/csma-cd/csma-cd-net-device.cc b/src/devices/csma/csma-net-device.cc similarity index 60% rename from src/devices/csma-cd/csma-cd-net-device.cc rename to src/devices/csma/csma-net-device.cc index b3527d354..531bf7314 100644 --- a/src/devices/csma-cd/csma-cd-net-device.cc +++ b/src/devices/csma/csma-net-device.cc @@ -25,52 +25,89 @@ #include "ns3/queue.h" #include "ns3/simulator.h" #include "ns3/composite-trace-resolver.h" -#include "csma-cd-net-device.h" -#include "csma-cd-channel.h" +#include "csma-net-device.h" +#include "csma-channel.h" #include "ns3/ethernet-header.h" #include "ns3/ethernet-trailer.h" #include "ns3/llc-snap-header.h" -NS_DEBUG_COMPONENT_DEFINE ("CsmaCdNetDevice"); +NS_DEBUG_COMPONENT_DEFINE ("CsmaNetDevice"); namespace ns3 { -CsmaCdNetDevice::CsmaCdNetDevice (Ptr node, MacAddress addr, - CsmaCdEncapsulationMode encapMode) - : NetDevice(node, addr), m_bps (DataRate (0xffffffff)) +CsmaTraceType::CsmaTraceType (enum Type type) + : m_type (type) +{} +CsmaTraceType::CsmaTraceType () + : m_type (RX) +{} +void +CsmaTraceType::Print (std::ostream &os) const { - NS_DEBUG ("CsmaCdNetDevice::CsmaCdNetDevice (" << node << ")"); + switch (m_type) { + case RX: + os << "dev-rx"; + break; + case DROP: + os << "dev-drop"; + break; + } +} +uint16_t +CsmaTraceType::GetUid (void) +{ + static uint16_t uid = AllocateUid ("CsmaTraceType"); + return uid; +} + + +CsmaNetDevice::CsmaNetDevice (Ptr node) + : NetDevice (node, Eui48Address::Allocate ()), + m_bps (DataRate (0xffffffff)) +{ + NS_DEBUG ("CsmaNetDevice::CsmaNetDevice (" << node << ")"); + m_encapMode = IP_ARP; + Init(true, true); +} + +CsmaNetDevice::CsmaNetDevice (Ptr node, Eui48Address addr, + CsmaEncapsulationMode encapMode) + : NetDevice(node, addr), + m_bps (DataRate (0xffffffff)) +{ + NS_DEBUG ("CsmaNetDevice::CsmaNetDevice (" << node << ")"); m_encapMode = encapMode; Init(true, true); } -CsmaCdNetDevice::CsmaCdNetDevice (Ptr node, MacAddress addr, - CsmaCdEncapsulationMode encapMode, +CsmaNetDevice::CsmaNetDevice (Ptr node, Eui48Address addr, + CsmaEncapsulationMode encapMode, bool sendEnable, bool receiveEnable) - : NetDevice(node, addr), m_bps (DataRate (0xffffffff)) + : NetDevice(node, addr), + m_bps (DataRate (0xffffffff)) { - NS_DEBUG ("CsmaCdNetDevice::CsmaCdNetDevice (" << node << ")"); + NS_DEBUG ("CsmaNetDevice::CsmaNetDevice (" << node << ")"); m_encapMode = encapMode; Init(sendEnable, receiveEnable); } -CsmaCdNetDevice::~CsmaCdNetDevice() +CsmaNetDevice::~CsmaNetDevice() { - NS_DEBUG ("CsmaCdNetDevice::~CsmaCdNetDevice ()"); + NS_DEBUG ("CsmaNetDevice::~CsmaNetDevice ()"); m_queue = 0; } void -CsmaCdNetDevice::DoDispose () +CsmaNetDevice::DoDispose () { m_channel = 0; NetDevice::DoDispose (); } // -// Assignment operator for CsmaCdNetDevice. +// Assignment operator for CsmaNetDevice. // // This uses the non-obvious trick of taking the source net device passed by // value instead of by reference. This causes the copy constructor to be @@ -78,67 +115,66 @@ CsmaCdNetDevice::DoDispose () // here is to return the newly constructed net device. // /* -CsmaCdNetDevice& -CsmaCdNetDevice::operator= (const CsmaCdNetDevice nd) +CsmaNetDevice& +CsmaNetDevice::operator= (const CsmaNetDevice nd) { - NS_DEBUG ("CsmaCdNetDevice::operator= (" << &nd << ")"); + NS_DEBUG ("CsmaNetDevice::operator= (" << &nd << ")"); return *this; } */ void -CsmaCdNetDevice::Init(bool sendEnable, bool receiveEnable) +CsmaNetDevice::Init(bool sendEnable, bool receiveEnable) { m_txMachineState = READY; m_tInterframeGap = Seconds(0); m_channel = 0; m_queue = 0; - EnableBroadcast (MacAddress ("ff:ff:ff:ff:ff:ff")); + EnableBroadcast (Eui48Address ("ff:ff:ff:ff:ff:ff")); EnableMulticast(); - EnablePointToPoint(); SetSendEnable (sendEnable); SetReceiveEnable (receiveEnable); } void -CsmaCdNetDevice::SetSendEnable (bool sendEnable) +CsmaNetDevice::SetSendEnable (bool sendEnable) { m_sendEnable = sendEnable; } void -CsmaCdNetDevice::SetReceiveEnable (bool receiveEnable) +CsmaNetDevice::SetReceiveEnable (bool receiveEnable) { m_receiveEnable = receiveEnable; } bool -CsmaCdNetDevice::IsSendEnabled (void) +CsmaNetDevice::IsSendEnabled (void) { return (m_sendEnable); } bool -CsmaCdNetDevice::IsReceiveEnabled (void) +CsmaNetDevice::IsReceiveEnabled (void) { return (m_receiveEnable); } void -CsmaCdNetDevice::SetDataRate (DataRate bps) +CsmaNetDevice::SetDataRate (DataRate bps) { m_bps = bps; } void -CsmaCdNetDevice::SetInterframeGap (Time t) +CsmaNetDevice::SetInterframeGap (Time t) { m_tInterframeGap = t; } void -CsmaCdNetDevice::SetBackoffParams (Time slotTime, uint32_t minSlots, +CsmaNetDevice::SetBackoffParams (Time slotTime, uint32_t minSlots, uint32_t maxSlots, uint32_t ceiling, uint32_t maxRetries) { @@ -149,7 +185,7 @@ CsmaCdNetDevice::SetBackoffParams (Time slotTime, uint32_t minSlots, m_backoff.m_maxRetries = maxRetries; } void -CsmaCdNetDevice::AddHeader (Packet& p, const MacAddress& dest, +CsmaNetDevice::AddHeader (Packet& p, Eui48Address dest, uint16_t protocolNumber) { if (m_encapMode == RAW) @@ -158,14 +194,15 @@ CsmaCdNetDevice::AddHeader (Packet& p, const MacAddress& dest, } EthernetHeader header (false); EthernetTrailer trailer; - header.SetSource(this->GetAddress()); + Eui48Address source = Eui48Address::ConvertFrom (GetAddress ()); + header.SetSource(source); header.SetDestination(dest); uint16_t lengthType = 0; switch (m_encapMode) { case ETHERNET_V1: - lengthType = p.GetSize() + header.GetSize() + trailer.GetSize(); + lengthType = p.GetSize() + header.GetSerializedSize() + trailer.GetSerializedSize(); break; case IP_ARP: lengthType = protocolNumber; @@ -185,7 +222,7 @@ CsmaCdNetDevice::AddHeader (Packet& p, const MacAddress& dest, p.AddTrailer(trailer); } bool -CsmaCdNetDevice::ProcessHeader (Packet& p, uint16_t & param) +CsmaNetDevice::ProcessHeader (Packet& p, uint16_t & param) { if (m_encapMode == RAW) { @@ -198,8 +235,8 @@ CsmaCdNetDevice::ProcessHeader (Packet& p, uint16_t & param) trailer.CheckFcs(p); p.RemoveHeader(header); - if ((header.GetDestination() != this->GetBroadcast()) && - (header.GetDestination() != this->GetAddress())) + if ((header.GetDestination() != GetBroadcast ()) && + (header.GetDestination() != GetAddress ())) { return false; } @@ -223,7 +260,7 @@ CsmaCdNetDevice::ProcessHeader (Packet& p, uint16_t & param) } bool -CsmaCdNetDevice::DoNeedsArp (void) const +CsmaNetDevice::DoNeedsArp (void) const { if ((m_encapMode == IP_ARP) || (m_encapMode == LLC)) { @@ -236,10 +273,14 @@ CsmaCdNetDevice::DoNeedsArp (void) const } bool -CsmaCdNetDevice::SendTo (Packet& p, const MacAddress& dest, uint16_t protocolNumber) +CsmaNetDevice::SendTo ( + const Packet& packet, + const Address& dest, + uint16_t protocolNumber) { - NS_DEBUG ("CsmaCdNetDevice::SendTo (" << &p << ")"); - NS_DEBUG ("CsmaCdNetDevice::SendTo (): UID is " << p.GetUid () << ")"); + Packet p = packet; + NS_DEBUG ("CsmaNetDevice::SendTo (" << &p << ")"); + NS_DEBUG ("CsmaNetDevice::SendTo (): UID is " << p.GetUid () << ")"); NS_ASSERT (IsLinkUp ()); @@ -247,7 +288,8 @@ CsmaCdNetDevice::SendTo (Packet& p, const MacAddress& dest, uint16_t protocolNum if (!IsSendEnabled()) return false; - AddHeader(p, dest, protocolNumber); + Eui48Address destination = Eui48Address::ConvertFrom (dest); + AddHeader(p, destination, protocolNumber); // Place the packet to be sent on the send queue if (m_queue->Enqueue(p) == false ) @@ -269,10 +311,10 @@ CsmaCdNetDevice::SendTo (Packet& p, const MacAddress& dest, uint16_t protocolNum } void -CsmaCdNetDevice::TransmitStart () +CsmaNetDevice::TransmitStart () { - NS_DEBUG ("CsmaCdNetDevice::TransmitStart (" << &m_currentPkt << ")"); - NS_DEBUG ("CsmaCdNetDevice::TransmitStart (): UID is " + NS_DEBUG ("CsmaNetDevice::TransmitStart (" << &m_currentPkt << ")"); + NS_DEBUG ("CsmaNetDevice::TransmitStart (): UID is " << m_currentPkt.GetUid () << ")"); // // This function is called to start the process of transmitting a packet. @@ -300,12 +342,12 @@ CsmaCdNetDevice::TransmitStart () m_backoff.IncrNumRetries(); Time backoffTime = m_backoff.GetBackoffTime(); - NS_DEBUG ("CsmaCdNetDevice::TransmitStart (): " + NS_DEBUG ("CsmaNetDevice::TransmitStart (): " << "Channel busy, backing off for " << backoffTime.GetSeconds () << "sec"); Simulator::Schedule (backoffTime, - &CsmaCdNetDevice::TransmitStart, + &CsmaNetDevice::TransmitStart, this); } } @@ -315,16 +357,16 @@ CsmaCdNetDevice::TransmitStart () m_txMachineState = BUSY; Time tEvent = Seconds (m_bps.CalculateTxTime(m_currentPkt.GetSize())); - NS_DEBUG ("CsmaCdNetDevice::TransmitStart (): " << + NS_DEBUG ("CsmaNetDevice::TransmitStart (): " << "Schedule TransmitCompleteEvent in " << tEvent.GetSeconds () << "sec"); Simulator::Schedule (tEvent, - &CsmaCdNetDevice::TransmitCompleteEvent, + &CsmaNetDevice::TransmitCompleteEvent, this); if (!m_channel->TransmitStart (m_currentPkt, m_deviceId)) { - NS_DEBUG ("CsmaCdNetDevice::TransmitStart (): " << + NS_DEBUG ("CsmaNetDevice::TransmitStart (): " << "Channel transmit start did not work at " << tEvent.GetSeconds () << "sec"); m_txMachineState = READY; @@ -339,11 +381,11 @@ CsmaCdNetDevice::TransmitStart () void -CsmaCdNetDevice::TransmitAbort (void) +CsmaNetDevice::TransmitAbort (void) { - NS_DEBUG ("CsmaCdNetDevice::TransmitAbort ()"); + NS_DEBUG ("CsmaNetDevice::TransmitAbort ()"); - NS_DEBUG ("CsmaCdNetDevice::TransmitAbort (): Pkt UID is " << + NS_DEBUG ("CsmaNetDevice::TransmitAbort (): Pkt UID is " << m_currentPkt.GetUid () << ")"); // Try to transmit a new packet @@ -356,9 +398,9 @@ CsmaCdNetDevice::TransmitAbort (void) } void -CsmaCdNetDevice::TransmitCompleteEvent (void) +CsmaNetDevice::TransmitCompleteEvent (void) { - NS_DEBUG ("CsmaCdNetDevice::TransmitCompleteEvent ()"); + NS_DEBUG ("CsmaNetDevice::TransmitCompleteEvent ()"); // // This function is called to finish the process of transmitting a packet. // We need to tell the channel that we've stopped wiggling the wire and @@ -370,24 +412,24 @@ CsmaCdNetDevice::TransmitCompleteEvent (void) NS_ASSERT(m_channel->GetState() == TRANSMITTING); m_txMachineState = GAP; - NS_DEBUG ("CsmaCdNetDevice::TransmitCompleteEvent (): Pkt UID is " << + NS_DEBUG ("CsmaNetDevice::TransmitCompleteEvent (): Pkt UID is " << m_currentPkt.GetUid () << ")"); m_channel->TransmitEnd (); NS_DEBUG ( - "CsmaCdNetDevice::TransmitCompleteEvent (): " << + "CsmaNetDevice::TransmitCompleteEvent (): " << "Schedule TransmitReadyEvent in " << m_tInterframeGap.GetSeconds () << "sec"); Simulator::Schedule (m_tInterframeGap, - &CsmaCdNetDevice::TransmitReadyEvent, + &CsmaNetDevice::TransmitReadyEvent, this); } void -CsmaCdNetDevice::TransmitReadyEvent (void) +CsmaNetDevice::TransmitReadyEvent (void) { - NS_DEBUG ("CsmaCdNetDevice::TransmitReadyEvent ()"); + NS_DEBUG ("CsmaNetDevice::TransmitReadyEvent ()"); // // This function is called to enable the transmitter after the interframe // gap has passed. If there are pending transmissions, we use this opportunity @@ -411,23 +453,25 @@ CsmaCdNetDevice::TransmitReadyEvent (void) } TraceResolver * -CsmaCdNetDevice::DoCreateTraceResolver (TraceContext const &context) +CsmaNetDevice::DoCreateTraceResolver (TraceContext const &context) { CompositeTraceResolver *resolver = new CompositeTraceResolver (context); resolver->Add ("queue", MakeCallback (&Queue::CreateTraceResolver, - PeekPointer (m_queue)), - CsmaCdNetDevice::QUEUE); + PeekPointer (m_queue))); resolver->Add ("rx", m_rxTrace, - CsmaCdNetDevice::RX); - return resolver; + CsmaTraceType (CsmaTraceType::RX)); + resolver->Add ("drop", + m_dropTrace, + CsmaTraceType (CsmaTraceType::DROP)); + return resolver; } bool -CsmaCdNetDevice::Attach (Ptr ch) +CsmaNetDevice::Attach (Ptr ch) { - NS_DEBUG ("CsmaCdNetDevice::Attach (" << &ch << ")"); + NS_DEBUG ("CsmaNetDevice::Attach (" << &ch << ")"); m_channel = ch; @@ -443,44 +487,86 @@ CsmaCdNetDevice::Attach (Ptr ch) } void -CsmaCdNetDevice::AddQueue (Ptr q) +CsmaNetDevice::AddQueue (Ptr q) { - NS_DEBUG ("CsmaCdNetDevice::AddQueue (" << q << ")"); + NS_DEBUG ("CsmaNetDevice::AddQueue (" << q << ")"); m_queue = q; } void -CsmaCdNetDevice::Receive (Packet& p) +CsmaNetDevice::Receive (const Packet& packet) { - NS_DEBUG ("CsmaCdNetDevice::Receive UID is (" << p.GetUid() << ")"); + EthernetHeader header (false); + EthernetTrailer trailer; + Eui48Address broadcast; + Eui48Address destination; + Packet p = packet; + + NS_DEBUG ("CsmaNetDevice::Receive UID is (" << p.GetUid() << ")"); // Only receive if send side of net device is enabled if (!IsReceiveEnabled()) - return; - - uint16_t param = 0; - Packet packet = p; - - if (ProcessHeader(packet, param)) { - m_rxTrace (packet); - ForwardUp (packet, param); - } - else - { - m_dropTrace (packet); + m_dropTrace (p); + return; } + + if (m_encapMode == RAW) + { + ForwardUp (packet, 0, GetBroadcast ()); + m_dropTrace (p); + return; + } + p.RemoveTrailer(trailer); + trailer.CheckFcs(p); + p.RemoveHeader(header); + + broadcast = Eui48Address::ConvertFrom (GetBroadcast ()); + destination = Eui48Address::ConvertFrom (GetAddress ()); + if ((header.GetDestination() != broadcast) && + (header.GetDestination() != destination)) + { + // not for us. + m_dropTrace (p); + return; + } + + m_rxTrace (p); +// +// protocol must be initialized to avoid a compiler warning in the RAW +// case that breaks the optimized build. +// + uint16_t protocol = 0; + + switch (m_encapMode) + { + case ETHERNET_V1: + case IP_ARP: + protocol = header.GetLengthType(); + break; + case LLC: { + LlcSnapHeader llc; + p.RemoveHeader (llc); + protocol = llc.GetType (); + } break; + case RAW: + NS_ASSERT (false); + break; + } + + ForwardUp (p, protocol, header.GetSource ()); + return; } Ptr -CsmaCdNetDevice::GetQueue(void) const +CsmaNetDevice::GetQueue(void) const { return m_queue; } Ptr -CsmaCdNetDevice::DoGetChannel(void) const +CsmaNetDevice::DoGetChannel(void) const { return m_channel; } diff --git a/src/devices/csma-cd/csma-cd-net-device.h b/src/devices/csma/csma-net-device.h similarity index 78% rename from src/devices/csma-cd/csma-cd-net-device.h rename to src/devices/csma/csma-net-device.h index c3ef7586e..62bbceab5 100644 --- a/src/devices/csma-cd/csma-cd-net-device.h +++ b/src/devices/csma/csma-net-device.h @@ -19,13 +19,13 @@ * Derived from the p2p net device file */ -#ifndef CSMA_CD_NET_DEVICE_H -#define CSMA_CD_NET_DEVICE_H +#ifndef CSMA_NET_DEVICE_H +#define CSMA_NET_DEVICE_H #include #include "ns3/node.h" #include "ns3/backoff.h" -#include "ns3/mac-address.h" +#include "ns3/address.h" #include "ns3/net-device.h" #include "ns3/callback.h" #include "ns3/packet.h" @@ -34,74 +34,97 @@ #include "ns3/data-rate.h" #include "ns3/ptr.h" #include "ns3/random-variable.h" +#include "ns3/eui48-address.h" namespace ns3 { class Queue; -class CsmaCdChannel; +class CsmaChannel; + +class CsmaTraceType : public TraceContextElement +{ +public: + enum Type { + RX, + DROP + }; + CsmaTraceType (enum Type type); + CsmaTraceType (); + void Print (std::ostream &os) const; + static uint16_t GetUid (void); +private: + enum Type m_type; +}; /** - * \class CsmaCdNetDevice - * \brief A Device for a CsmaCd Network Link. + * \class CsmaNetDevice + * \brief A Device for a Csma Network Link. * - * The Csma/Cd net device class is analogous to layer 1 and 2 of the + * The Csma net device class is analogous to layer 1 and 2 of the * TCP stack. The NetDevice takes a raw packet of bytes and creates a - * protocol specific packet from them. The Csma/Cd net device class + * protocol specific packet from them. The Csma net device class * takes this packet and adds and processes the headers/trailers that * are associated with EthernetV1, EthernetV2, RAW or LLC * protocols. The EthernetV1 packet type adds and removes Ethernet * destination and source addresses. The LLC packet type adds and * removes LLC snap headers. The raw packet type does not add or - * remove any headers. Each Csma/Cd net device will receive all - * packets written to the Csma/Cd link. The ProcessHeader function can + * remove any headers. Each Csma net device will receive all + * packets written to the Csma link. The ProcessHeader function can * be used to filter out the packets such that higher level layers * only receive packets that are addressed to their associated net * devices * */ -class CsmaCdNetDevice : public NetDevice { +class CsmaNetDevice : public NetDevice { public: - /** - * Enumeration of the types of traces supported in the class. - * - */ - enum TraceType { - QUEUE, /**< Trace queue events on the attached queue */ - RX, /**< Trace packet reception events (from the channel) */ - DROP /**< Trace packet drop events (from the channel) */ - }; /** * Enumeration of the types of packets supported in the class. * */ -enum CsmaCdEncapsulationMode { +enum CsmaEncapsulationMode { ETHERNET_V1, /**< Version one ethernet packet, length field */ IP_ARP, /**< Ethernet packet encapsulates IP/ARP packet */ RAW, /**< Packet that contains no headers */ LLC, /**< LLC packet encapsulation */ }; + CsmaNetDevice (Ptr node); /** - * Construct a CsmaCdNetDevice + * Construct a CsmaNetDevice * - * This is the constructor for the CsmaCdNetDevice. It takes as a + * This is the constructor for the CsmaNetDevice. It takes as a * parameter the Node to which this device is connected. Ownership of the * Node pointer is not implied and the node must not be deleted. * * \param node the Node to which this device is connected. * \param addr The source MAC address of the net device. + * \param pktType the type of encapsulation */ - CsmaCdNetDevice (Ptr node, MacAddress addr, CsmaCdEncapsulationMode pktType); - CsmaCdNetDevice (Ptr node, MacAddress addr, - CsmaCdEncapsulationMode pktType, + CsmaNetDevice (Ptr node, Eui48Address addr, CsmaEncapsulationMode pktType); + + /** + * Construct a CsmaNetDevice + * + * This is the constructor for the CsmaNetDevice. It takes as a + * parameter the Node to which this device is connected. Ownership of the + * Node pointer is not implied and the node must not be deleted. + * + * \param node the Node to which this device is connected. + * \param addr The source MAC address of the net device. + * \param pktType the type of encapsulation + * \param sendEnable whether this device is able to send + * \param receiveEnable whether this device is able to receive + */ + CsmaNetDevice (Ptr node, Eui48Address addr, + CsmaEncapsulationMode pktType, bool sendEnable, bool receiveEnable); /** - * Destroy a CsmaCdNetDevice + * Destroy a CsmaNetDevice * - * This is the destructor for the CsmaCdNetDevice. + * This is the destructor for the CsmaNetDevice. */ - virtual ~CsmaCdNetDevice(); + virtual ~CsmaNetDevice(); /** * Set the Data Rate used for transmission of packets. The data rate is * set in the Attach () method from the corresponding field in the channel @@ -138,23 +161,23 @@ enum CsmaCdEncapsulationMode { /** * Attach the device to a channel. * - * The function Attach is used to add a CsmaCdNetDevice to a - * CsmaCdChannel. + * The function Attach is used to add a CsmaNetDevice to a + * CsmaChannel. * * @see SetDataRate () * @see SetInterframeGap () * \param ch a pointer to the channel to which this object is being attached. */ - bool Attach (Ptr ch); + bool Attach (Ptr ch); /** - * Attach a queue to the CsmaCdNetDevice. + * Attach a queue to the CsmaNetDevice. * - * The CsmaCdNetDevice "owns" a queue. This queue is created by the - * CsmaCdTopology object and implements a queueing method such as - * DropTail or RED. The CsmaCdNetDevice assumes ownership of this + * The CsmaNetDevice "owns" a queue. This queue is created by the + * CsmaTopology object and implements a queueing method such as + * DropTail or RED. The CsmaNetDevice assumes ownership of this * queue and must delete it when the device is destroyed. * - * @see CsmaCdTopology::AddCsmaCdLink () + * @see CsmaTopology::AddCsmaLink () * @see Queue * @see DropTailQueue * \param queue a pointer to the queue for which object is assuming @@ -162,17 +185,17 @@ enum CsmaCdEncapsulationMode { */ void AddQueue (Ptr queue); /** - * Receive a packet from a connected CsmaCdChannel. + * Receive a packet from a connected CsmaChannel. * - * The CsmaCdNetDevice receives packets from its connected channel + * The CsmaNetDevice receives packets from its connected channel * and forwards them up the protocol stack. This is the public method * used by the channel to indicate that the last bit of a packet has * arrived at the device. * - * @see CsmaCdChannel + * @see CsmaChannel * \param p a reference to the received packet */ - void Receive (Packet& p); + void Receive (const Packet& p); bool IsSendEnabled (void); bool IsReceiveEnabled (void); @@ -210,7 +233,7 @@ protected: * \param protocolNumber In some protocols, identifies the type of * payload contained in this packet. */ - void AddHeader (Packet& p, const MacAddress& dest, + void AddHeader (Packet& p, Eui48Address dest, uint16_t protocolNumber); /** * Removes, from a packet of data, all headers and trailers that @@ -226,14 +249,14 @@ protected: private: // disable copy constructor and operator = - CsmaCdNetDevice &operator = (const CsmaCdNetDevice &o); - CsmaCdNetDevice (const CsmaCdNetDevice &o); + CsmaNetDevice &operator = (const CsmaNetDevice &o); + CsmaNetDevice (const CsmaNetDevice &o); /** * Initializes variablea when construction object. */ void Init (bool sendEnable, bool receiveEnable); /** - * Send a Packet on the Csma/Cd network + * Send a Packet on the Csma network * * This method does not use a destination address since all packets * are broadcast to all NetDevices attached to the channel. Packet @@ -247,13 +270,13 @@ private: * \param protocolNumber -- this parameter is not used here * \return true if success, false on failure */ - virtual bool SendTo (Packet& p, const MacAddress& dest, uint16_t protocolNumber); + virtual bool SendTo (const Packet& p, const Address& dest, uint16_t protocolNumber); /** * Start Sending a Packet Down the Wire. * * The TransmitStart method is the method that is used internally in - * the CsmaCdNetDevice to begin the process of sending a packet + * the CsmaNetDevice to begin the process of sending a packet * out on the channel. The corresponding method is called on the * channel to let it know that the physical device this class * represents has virually started sending signals, this causes the @@ -262,7 +285,7 @@ private: * is busy, the method reschedules itself for a later time (within * the backoff period) * - * @see CsmaCdChannel::TransmitStart () + * @see CsmaChannel::TransmitStart () * @see TransmitCompleteEvent () */ void TransmitStart (); @@ -279,7 +302,7 @@ private: * also schedules the TransmitReadyEvent at which time the transmitter * becomes ready to send the next packet. * - * @see CsmaCdChannel::TransmitEnd () + * @see CsmaChannel::TransmitEnd () * @see TransmitReadyEvent () */ void TransmitCompleteEvent (void); @@ -351,7 +374,7 @@ private: * function and that should be processed by the ProcessHeader * function. */ - CsmaCdEncapsulationMode m_encapMode; + CsmaEncapsulationMode m_encapMode; /** * The data rate that the Net Device uses to simulate packet transmission * timing. @@ -377,14 +400,14 @@ private: */ Packet m_currentPkt; /** - * The CsmaCdChannel to which this CsmaCdNetDevice has been + * The CsmaChannel to which this CsmaNetDevice has been * attached. - * @see class CsmaCdChannel + * @see class CsmaChannel */ - Ptr m_channel; + Ptr m_channel; /** - * The Queue which this CsmaCdNetDevice uses as a packet source. - * Management of this Queue has been delegated to the CsmaCdNetDevice + * The Queue which this CsmaNetDevice uses as a packet source. + * Management of this Queue has been delegated to the CsmaNetDevice * and it has the responsibility for deletion. * @see class Queue * @see class DropTailQueue @@ -398,12 +421,12 @@ private: * @see class CallBackTraceSource * @see class TraceResolver */ - CallbackTraceSource m_rxTrace; - CallbackTraceSource m_dropTrace; + CallbackTraceSource m_rxTrace; + CallbackTraceSource m_dropTrace; }; }; // namespace ns3 -#endif // CSMA_CD_NET_DEVICE_H +#endif // CSMA_NET_DEVICE_H diff --git a/src/devices/csma-cd/csma-cd-topology.cc b/src/devices/csma/csma-topology.cc similarity index 67% rename from src/devices/csma-cd/csma-cd-topology.cc rename to src/devices/csma/csma-topology.cc index e4af1a309..27e4e9e29 100644 --- a/src/devices/csma-cd/csma-cd-topology.cc +++ b/src/devices/csma/csma-topology.cc @@ -19,38 +19,38 @@ // // -// Topology helper for CsmaCd channels in ns3. +// Topology helper for Csma channels in ns3. #include "ns3/assert.h" #include "ns3/debug.h" #include "ns3/queue.h" -#include "csma-cd-channel.h" -#include "csma-cd-net-device.h" -#include "csma-cd-topology.h" +#include "csma-channel.h" +#include "csma-net-device.h" +#include "csma-topology.h" #include "ns3/socket-factory.h" namespace ns3 { -Ptr -CsmaCdTopology::CreateCsmaCdChannel( +Ptr +CsmaTopology::CreateCsmaChannel( const DataRate& bps, const Time& delay) { - Ptr channel = Create (bps, delay); + Ptr channel = Create (bps, delay); return channel; } #if 0 -Ptr -CsmaCdTopology::AddCsmaCdEthernetNode( +Ptr +CsmaTopology::AddCsmaEthernetNode( Ptr n1, - Ptr ch, + Ptr ch, MacAddress addr) { - Ptr nd1 = Create (n1, addr, - ns3::CsmaCdNetDevice::ETHERNET_V1); + Ptr nd1 = Create (n1, addr, + ns3::CsmaNetDevice::ETHERNET_V1); Ptr q = Queue::CreateDefault (); nd1->AddQueue(q); @@ -60,9 +60,9 @@ CsmaCdTopology::AddCsmaCdEthernetNode( } Ptr -CsmaCdTopology::ConnectPacketSocket(Ptr app, - Ptr ndSrc, - Ptr ndDest) +CsmaTopology::ConnectPacketSocket(Ptr app, + Ptr ndSrc, + Ptr ndDest) { Ptr socket = Create (); socket->Bind(ndSrc); @@ -73,8 +73,8 @@ CsmaCdTopology::ConnectPacketSocket(Ptr app, } Ptr -CsmaCdTopology::ConnectPacketSocket(Ptr app, - Ptr ndSrc, +CsmaTopology::ConnectPacketSocket(Ptr app, + Ptr ndSrc, MacAddress macAddr) { Ptr socket = Create (); @@ -86,7 +86,7 @@ CsmaCdTopology::ConnectPacketSocket(Ptr app, } Ptr -CsmaCdTopology::CreatePacketSocket(Ptr n1, std::string iid_name) +CsmaTopology::CreatePacketSocket(Ptr n1, std::string iid_name) { InterfaceId iid = InterfaceId::LookupByName (iid_name); diff --git a/src/devices/csma-cd/csma-cd-topology.h b/src/devices/csma/csma-topology.h similarity index 78% rename from src/devices/csma-cd/csma-cd-topology.h rename to src/devices/csma/csma-topology.h index b89e0b522..9f39b73ef 100644 --- a/src/devices/csma-cd/csma-cd-topology.h +++ b/src/devices/csma/csma-topology.h @@ -19,33 +19,28 @@ // // Topology helper for multipoint channels in ns3. // -#ifndef CSMA_CD_TOPOLOGY_H -#define CSMA_CD_TOPOLOGY_H +#ifndef CSMA_TOPOLOGY_H +#define CSMA_TOPOLOGY_H #include "ns3/ptr.h" -#include "ns3/csma-cd-net-device.h" -#if 0 -#include "ns3/packet-socket.h" -#include "ns3/packet-socket-app.h" -#endif +#include "ns3/csma-net-device.h" #include "ns3/node.h" -#include "ns3/mac-address.h" // The topology class consists of only static methods thar are used to // create the topology and data flows for an ns3 simulation namespace ns3 { -class CsmaCdChannel; +class CsmaChannel; class Node; class DataRate; class Queue; /** - * \brief A helper class to create CsmaCd Topologies + * \brief A helper class to create Csma Topologies * - * CsmaCd topologies are created based on the - * ns3::CsmaCdNetDevice subclasses and ns3::CsmaCdChannel + * Csma topologies are created based on the + * ns3::CsmaNetDevice subclasses and ns3::CsmaChannel * objects. This class uses the EthernetNetDevice and * PacketSocket classes in order to create logical connections between * net devices. The PacketSocket class generates the data and the @@ -54,30 +49,30 @@ class Queue; * EthernetNetDevice class filters received data packets * according to its destination Mac addresses. */ -class CsmaCdTopology { +class CsmaTopology { public: /** * \param dataRate Maximum transmission link rate * \param delay propagation delay between any two nodes - * \return Pointer to the created CsmaCdChannel + * \return Pointer to the created CsmaChannel * - * Create a CsmaCdChannel. All nodes connected to a multipoint + * Create a CsmaChannel. All nodes connected to a multipoint * channels will receive all packets written to that channel */ - static Ptr CreateCsmaCdChannel( + static Ptr CreateCsmaChannel( const DataRate& dataRate, const Time& delay); #if 0 /** * \param n1 Node to be attached to the multipoint channel - * \param ch CsmaCdChannel to which node n1 should be attached + * \param ch CsmaChannel to which node n1 should be attached * \param addr MacAddress that should be assigned to the * EthernetNetDevice that will be added to the node. * * Add a multipoint node to a multipoint channel */ - static Ptr AddCsmaCdEthernetNode(Ptr n1, - Ptr ch, + static Ptr AddCsmaEthernetNode(Ptr n1, + Ptr ch, MacAddress addr); /** @@ -91,8 +86,8 @@ public: * two net devices */ static Ptr ConnectPacketSocket(Ptr app, - Ptr ndSrc, - Ptr ndDest); + Ptr ndSrc, + Ptr ndDest); /** * \param app Application that will be sending data to the agent @@ -106,7 +101,7 @@ static Ptr ConnectPacketSocket(Ptr app, * net device to a destination MacAddress */ static Ptr ConnectPacketSocket(Ptr app, - Ptr ndSrc, + Ptr ndSrc, MacAddress macAddr); /** diff --git a/src/devices/csma/wscript b/src/devices/csma/wscript new file mode 100644 index 000000000..8257c06dc --- /dev/null +++ b/src/devices/csma/wscript @@ -0,0 +1,19 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +def build(bld): + obj = bld.create_ns3_module('csma', ['node']) + obj.source = [ + 'backoff.cc', + 'csma-net-device.cc', + 'csma-channel.cc', + 'csma-topology.cc', + 'csma-ipv4-topology.cc', + ] + headers = bld.create_obj('ns3header') + headers.source = [ + 'backoff.h', + 'csma-net-device.h', + 'csma-channel.h', + 'csma-topology.h', + 'csma-ipv4-topology.h', + ] diff --git a/src/devices/point-to-point/point-to-point-net-device.cc b/src/devices/point-to-point/point-to-point-net-device.cc index fff158521..a071f9246 100644 --- a/src/devices/point-to-point/point-to-point-net-device.cc +++ b/src/devices/point-to-point/point-to-point-net-device.cc @@ -26,9 +26,10 @@ #include "ns3/queue.h" #include "ns3/simulator.h" #include "ns3/composite-trace-resolver.h" +#include "ns3/eui48-address.h" +#include "ns3/llc-snap-header.h" #include "point-to-point-net-device.h" #include "point-to-point-channel.h" -#include "ns3/llc-snap-header.h" NS_DEBUG_COMPONENT_DEFINE ("PointToPointNetDevice"); @@ -39,10 +40,25 @@ DataRateDefaultValue PointToPointNetDevice::g_defaultRate( "The default data rate for point to point links", DataRate ("10Mb/s")); - PointToPointNetDevice::PointToPointNetDevice (Ptr node, - const DataRate& rate) +PointToPointTraceType::PointToPointTraceType () +{} +void +PointToPointTraceType::Print (std::ostream &os) const +{ + os << "dev-rx"; +} +uint16_t +PointToPointTraceType::GetUid (void) +{ + static uint16_t uid = AllocateUid ("PointToPointTraceType"); + return uid; +} + + +PointToPointNetDevice::PointToPointNetDevice (Ptr node, + const DataRate& rate) : - NetDevice(node, MacAddress (6)), + NetDevice(node, Eui48Address::Allocate ()), m_txMachineState (READY), m_bps (rate), m_tInterframeGap (Seconds(0)), @@ -55,7 +71,7 @@ DataRateDefaultValue PointToPointNetDevice::g_defaultRate( // BUGBUG FIXME // // You _must_ support broadcast to get any sort of packet from the ARP layer. - EnableBroadcast (MacAddress ("ff:ff:ff:ff:ff:ff")); + EnableBroadcast (Eui48Address ("ff:ff:ff:ff:ff:ff")); EnableMulticast(); EnablePointToPoint(); } @@ -66,15 +82,16 @@ PointToPointNetDevice::~PointToPointNetDevice() m_queue = 0; } -void PointToPointNetDevice::AddHeader(Packet& p, const MacAddress& dest, - uint16_t protocolNumber) +void +PointToPointNetDevice::AddHeader(Packet& p, uint16_t protocolNumber) { LlcSnapHeader llc; llc.SetType (protocolNumber); p.AddHeader (llc); } -bool PointToPointNetDevice::ProcessHeader(Packet& p, uint16_t& param) +bool +PointToPointNetDevice::ProcessHeader(Packet& p, uint16_t& param) { LlcSnapHeader llc; p.RemoveHeader (llc); @@ -100,9 +117,10 @@ void PointToPointNetDevice::SetInterframeGap(const Time& t) m_tInterframeGap = t; } -bool PointToPointNetDevice::SendTo (Packet& p, const MacAddress& dest, +bool PointToPointNetDevice::SendTo (const Packet& packet, const Address& dest, uint16_t protocolNumber) { + Packet p = packet; NS_DEBUG ("PointToPointNetDevice::SendTo (" << &p << ", " << &dest << ")"); NS_DEBUG ("PointToPointNetDevice::SendTo (): UID is " << p.GetUid () << ")"); @@ -110,7 +128,7 @@ bool PointToPointNetDevice::SendTo (Packet& p, const MacAddress& dest, // "go down" during the simulation? Shouldn't we just wait for it // to come back up? NS_ASSERT (IsLinkUp ()); - AddHeader(p, dest, protocolNumber); + AddHeader(p, protocolNumber); // // This class simulates a point to point device. In the case of a serial @@ -119,14 +137,14 @@ bool PointToPointNetDevice::SendTo (Packet& p, const MacAddress& dest, // // If there's a transmission in progress, we enque the packet for later // trnsmission; otherwise we send it now. - if (m_txMachineState == READY) - { - return TransmitStart (p); - } - else - { - return m_queue->Enqueue(p); - } + if (m_txMachineState == READY) + { + return TransmitStart (p); + } + else + { + return m_queue->Enqueue(p); + } } bool @@ -176,11 +194,10 @@ TraceResolver* PointToPointNetDevice::DoCreateTraceResolver ( { CompositeTraceResolver *resolver = new CompositeTraceResolver (context); resolver->Add ("queue", - MakeCallback (&Queue::CreateTraceResolver, PeekPointer (m_queue)), - PointToPointNetDevice::QUEUE); + MakeCallback (&Queue::CreateTraceResolver, PeekPointer (m_queue))); resolver->Add ("rx", m_rxTrace, - PointToPointNetDevice::RX); + PointToPointTraceType ()); return resolver; } @@ -218,12 +235,12 @@ void PointToPointNetDevice::AddQueue (Ptr q) void PointToPointNetDevice::Receive (Packet& p) { NS_DEBUG ("PointToPointNetDevice::Receive (" << &p << ")"); - uint16_t param = 0; + uint16_t protocol = 0; Packet packet = p; - ProcessHeader(packet, param); m_rxTrace (packet); - ForwardUp (packet, param); + ProcessHeader(packet, protocol); + ForwardUp (packet, protocol, GetBroadcast ()); } Ptr PointToPointNetDevice::GetQueue(void) const diff --git a/src/devices/point-to-point/point-to-point-net-device.h b/src/devices/point-to-point/point-to-point-net-device.h index 2cdafab28..fa3d3b838 100644 --- a/src/devices/point-to-point/point-to-point-net-device.h +++ b/src/devices/point-to-point/point-to-point-net-device.h @@ -22,7 +22,7 @@ #define POINT_TO_POINT_NET_DEVICE_H #include -#include "ns3/mac-address.h" +#include "ns3/address.h" #include "ns3/node.h" #include "ns3/net-device.h" #include "ns3/callback.h" @@ -38,6 +38,14 @@ namespace ns3 { class Queue; class PointToPointChannel; +class PointToPointTraceType : public TraceContextElement +{ +public: + PointToPointTraceType (); + void Print (std::ostream &os) const; + static uint16_t GetUid (void); +}; + /** * \class PointToPointNetDevice * \brief A Device for a Point to Point Network Link. @@ -63,14 +71,6 @@ class PointToPointChannel; */ class PointToPointNetDevice : public NetDevice { public: - /** - * Enumeration of the types of traces supported in the class. - * - */ - enum TraceType { - QUEUE, /**< Trace queue events on the attached queue */ - RX, /**< Trace packet reception events (from the channel) */ - }; /** * Construct a PointToPointNetDevice * @@ -175,7 +175,6 @@ protected: virtual Ptr DoGetChannel(void) const; /** * Set a new default data rate - * @param Data rate to set for new default */ static void SetDefaultRate(const DataRate&); @@ -191,7 +190,7 @@ private: * Adds the necessary headers and trailers to a packet of data in order to * respect the protocol implemented by the agent. */ - void AddHeader(Packet& p, const MacAddress& dest, uint16_t protocolNumber); + void AddHeader(Packet& p, uint16_t protocolNumber); /** * Removes, from a packet of data, all headers and trailers that * relate to the protocol implemented by the agent @@ -208,11 +207,11 @@ private: * * @see NetDevice * @param p a reference to the packet to send - * @param dest a reference to the MacAddress of the destination device + * @param dest a reference to the Address of the destination device * @param protocolNumber Protocol Number used to find protocol touse * @returns true if success, false on failure */ - virtual bool SendTo (Packet& p, const MacAddress& dest, + virtual bool SendTo (const Packet& p, const Address& dest, uint16_t protocolNumber); /** * Start Sending a Packet Down the Wire. @@ -291,7 +290,7 @@ private: * @see class CallBackTraceSource * @see class TraceResolver */ - CallbackTraceSource m_rxTrace; + CallbackTraceSource m_rxTrace; /** * Default data rate. Used for all newly created p2p net devices */ diff --git a/src/devices/point-to-point/wscript b/src/devices/point-to-point/wscript index 77c26fd80..c788a7880 100644 --- a/src/devices/point-to-point/wscript +++ b/src/devices/point-to-point/wscript @@ -2,10 +2,7 @@ def build(bld): - module = bld.create_obj('cpp', 'shlib') - module.name = 'ns3-point-to-point' - module.target = module.name - module.uselib_local = ['ns3-node'] + module = bld.create_ns3_module('point-to-point', ['node']) module.source = [ 'point-to-point-net-device.cc', 'point-to-point-channel.cc', diff --git a/src/internet-node/arp-cache.cc b/src/internet-node/arp-cache.cc index 92cb13af4..b4987daef 100644 --- a/src/internet-node/arp-cache.cc +++ b/src/internet-node/arp-cache.cc @@ -148,7 +148,7 @@ ArpCache::Entry::MarkDead (void) UpdateSeen (); } Packet -ArpCache::Entry::MarkAlive (MacAddress macAddress) +ArpCache::Entry::MarkAlive (Address macAddress) { NS_ASSERT (m_state == WAIT_REPLY); //NS_ASSERT (m_waiting != 0); @@ -182,7 +182,7 @@ ArpCache::Entry::MarkWaitReply (Packet waiting) UpdateSeen (); } -MacAddress +Address ArpCache::Entry::GetMacAddress (void) { NS_ASSERT (m_state == ALIVE); diff --git a/src/internet-node/arp-cache.h b/src/internet-node/arp-cache.h index 436d44eba..2cd88c85f 100644 --- a/src/internet-node/arp-cache.h +++ b/src/internet-node/arp-cache.h @@ -26,7 +26,7 @@ #include "ns3/nstime.h" #include "ns3/net-device.h" #include "ns3/ipv4-address.h" -#include "ns3/mac-address.h" +#include "ns3/address.h" #include "ns3/ptr.h" #include "sgi-hashmap.h" @@ -101,7 +101,7 @@ public: * \param macAddress * \return */ - Packet MarkAlive (MacAddress macAddress); + Packet MarkAlive (Address macAddress); /** * \param waiting */ @@ -127,7 +127,7 @@ public: /** * \return The MacAddress of this entry */ - MacAddress GetMacAddress (void); + Address GetMacAddress (void); /** * \return True if this entry has timedout; false otherwise. */ @@ -143,7 +143,7 @@ public: ArpCache *m_arp; ArpCacheEntryState_e m_state; Time m_lastSeen; - MacAddress m_macAddress; + Address m_macAddress; Packet m_waiting; }; diff --git a/src/internet-node/arp-header.cc b/src/internet-node/arp-header.cc index 7191b0299..4fdfe19d7 100644 --- a/src/internet-node/arp-header.cc +++ b/src/internet-node/arp-header.cc @@ -25,13 +25,19 @@ namespace ns3 { -ArpHeader::~ArpHeader () -{} +NS_HEADER_ENSURE_REGISTERED (ArpHeader); + +uint32_t +ArpHeader::GetUid (void) +{ + static uint32_t uid = AllocateUid ("ArpHeader.ns3"); + return uid; +} void -ArpHeader::SetRequest (MacAddress sourceHardwareAddress, +ArpHeader::SetRequest (Address sourceHardwareAddress, Ipv4Address sourceProtocolAddress, - MacAddress destinationHardwareAddress, + Address destinationHardwareAddress, Ipv4Address destinationProtocolAddress) { m_type = ARP_TYPE_REQUEST; @@ -41,9 +47,9 @@ ArpHeader::SetRequest (MacAddress sourceHardwareAddress, m_ipv4Dest = destinationProtocolAddress; } void -ArpHeader::SetReply (MacAddress sourceHardwareAddress, +ArpHeader::SetReply (Address sourceHardwareAddress, Ipv4Address sourceProtocolAddress, - MacAddress destinationHardwareAddress, + Address destinationHardwareAddress, Ipv4Address destinationProtocolAddress) { m_type = ARP_TYPE_REPLY; @@ -62,12 +68,12 @@ ArpHeader::IsReply (void) const { return (m_type == ARP_TYPE_REPLY)?true:false; } -MacAddress +Address ArpHeader::GetSourceHardwareAddress (void) { return m_macSource; } -MacAddress +Address ArpHeader::GetDestinationHardwareAddress (void) { return m_macDest; @@ -84,13 +90,13 @@ ArpHeader::GetDestinationIpv4Address (void) } std::string -ArpHeader::DoGetName (void) const +ArpHeader::GetName (void) const { return "ARP"; } void -ArpHeader::PrintTo (std::ostream &os) const +ArpHeader::Print (std::ostream &os) const { if (IsRequest ()) { @@ -123,7 +129,7 @@ ArpHeader::GetSerializedSize (void) const } void -ArpHeader::SerializeTo (Buffer::Iterator start) const +ArpHeader::Serialize (Buffer::Iterator start) const { Buffer::Iterator i = start; NS_ASSERT (m_macSource.GetLength () == m_macDest.GetLength ()); @@ -141,7 +147,7 @@ ArpHeader::SerializeTo (Buffer::Iterator start) const WriteTo (i, m_ipv4Dest); } uint32_t -ArpHeader::DeserializeFrom (Buffer::Iterator start) +ArpHeader::Deserialize (Buffer::Iterator start) { Buffer::Iterator i = start; i.Next (2+2); diff --git a/src/internet-node/arp-header.h b/src/internet-node/arp-header.h index 9f85579f5..509530991 100644 --- a/src/internet-node/arp-header.h +++ b/src/internet-node/arp-header.h @@ -23,59 +23,47 @@ #define ARP_HEADER_H #include "ns3/header.h" -#include "ns3/mac-address.h" +#include "ns3/address.h" #include "ns3/ipv4-address.h" +#include namespace ns3 { /** * \brief The packet header for an ARP packet */ -class ArpHeader : public Header { - public: - virtual ~ArpHeader (); +class ArpHeader : public Header +{ +public: + static uint32_t GetUid (void); - void SetRequest (MacAddress sourceHardwareAddress, + void SetRequest (Address sourceHardwareAddress, Ipv4Address sourceProtocolAddress, - MacAddress destinationHardwareAddress, + Address destinationHardwareAddress, Ipv4Address destinationProtocolAddress); - void SetReply (MacAddress sourceHardwareAddress, + void SetReply (Address sourceHardwareAddress, Ipv4Address sourceProtocolAddress, - MacAddress destinationHardwareAddress, + Address destinationHardwareAddress, Ipv4Address destinationProtocolAddress); bool IsRequest (void) const; bool IsReply (void) const; - MacAddress GetSourceHardwareAddress (void); - MacAddress GetDestinationHardwareAddress (void); + Address GetSourceHardwareAddress (void); + Address GetDestinationHardwareAddress (void); Ipv4Address GetSourceIpv4Address (void); Ipv4Address GetDestinationIpv4Address (void); -private: - virtual std::string DoGetName (void) const; - /** - * \param os - */ - virtual void PrintTo (std::ostream &os) const; - /** - * \return - */ - virtual uint32_t GetSerializedSize (void) const; - /** - * \param start - */ - virtual void SerializeTo (Buffer::Iterator start) const; - /** - * \param start - * \return - */ - virtual uint32_t DeserializeFrom (Buffer::Iterator start); + std::string GetName (void) const; + void Print (std::ostream &os) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator start) const; + uint32_t Deserialize (Buffer::Iterator start); enum ArpType_e { ARP_TYPE_REQUEST = 1, ARP_TYPE_REPLY = 2 }; uint16_t m_type; - MacAddress m_macSource; - MacAddress m_macDest; + Address m_macSource; + Address m_macDest; Ipv4Address m_ipv4Source; Ipv4Address m_ipv4Dest; }; diff --git a/src/internet-node/arp-ipv4-interface.cc b/src/internet-node/arp-ipv4-interface.cc index 4ca7ebbb3..efe767531 100644 --- a/src/internet-node/arp-ipv4-interface.cc +++ b/src/internet-node/arp-ipv4-interface.cc @@ -25,10 +25,11 @@ #include "ns3/composite-trace-resolver.h" #include "ns3/node.h" #include "ns3/net-device.h" +#include "ns3/address.h" #include "arp-ipv4-interface.h" -#include "arp-private.h" #include "ipv4-l3-protocol.h" +#include "arp-l3-protocol.h" namespace ns3 { @@ -46,8 +47,7 @@ ArpIpv4Interface::DoCreateTraceResolver (TraceContext const &context) if (GetDevice () != 0) { resolver->Add ("netdevice", - MakeCallback (&NetDevice::CreateTraceResolver, PeekPointer (GetDevice ())), - ArpIpv4Interface::NETDEVICE); + MakeCallback (&NetDevice::CreateTraceResolver, PeekPointer (GetDevice ()))); } return resolver; @@ -59,18 +59,17 @@ ArpIpv4Interface::SendTo (Packet p, Ipv4Address dest) NS_ASSERT (GetDevice () != 0); if (GetDevice ()->NeedsArp ()) { - Ptr arp = m_node->QueryInterface (ArpPrivate::iid); - MacAddress hardwareDestination; + Ptr arp = m_node->QueryInterface (ArpL3Protocol::iid); + Address hardwareDestination; bool found; if (dest.IsBroadcast ()) { - hardwareDestination = GetDevice ()->GetBroadcast (); - found = true; + hardwareDestination = GetDevice ()->GetBroadcast (); + found = true; } else { - Ptr arp = m_node->QueryInterface (ArpPrivate::iid); found = arp->Lookup (p, dest, GetDevice (), &hardwareDestination); } diff --git a/src/internet-node/arp-ipv4-interface.h b/src/internet-node/arp-ipv4-interface.h index b1c0698f1..7df522e01 100644 --- a/src/internet-node/arp-ipv4-interface.h +++ b/src/internet-node/arp-ipv4-interface.h @@ -39,10 +39,6 @@ class Node; class ArpIpv4Interface : public Ipv4Interface { public: - enum TraceType { - NETDEVICE, - ARP, - }; ArpIpv4Interface (Ptr node, Ptr device); virtual ~ArpIpv4Interface (); diff --git a/src/internet-node/arp-l3-protocol.cc b/src/internet-node/arp-l3-protocol.cc index b68995be3..100f1df88 100644 --- a/src/internet-node/arp-l3-protocol.cc +++ b/src/internet-node/arp-l3-protocol.cc @@ -24,22 +24,24 @@ #include "ns3/node.h" #include "ns3/net-device.h" +#include "ipv4-l3-protocol.h" #include "arp-l3-protocol.h" #include "arp-header.h" #include "arp-cache.h" #include "ipv4-interface.h" -#include "ipv4-private.h" NS_DEBUG_COMPONENT_DEFINE ("ArpL3Protocol"); namespace ns3 { +const InterfaceId ArpL3Protocol::iid = MakeInterfaceId ("ArpL3Protocol", Object::iid); const uint16_t ArpL3Protocol::PROT_NUMBER = 0x0806; ArpL3Protocol::ArpL3Protocol (Ptr node) - : L3Protocol (PROT_NUMBER, 0/* XXX: correct version number ? */ ), - m_node (node) -{} + : m_node (node) +{ + SetInterfaceId (ArpL3Protocol::iid); +} ArpL3Protocol::~ArpL3Protocol () {} @@ -53,7 +55,7 @@ ArpL3Protocol::DoDispose (void) } m_cacheList.clear (); m_node = 0; - L3Protocol::DoDispose (); + Object::DoDispose (); } TraceResolver * @@ -72,7 +74,7 @@ ArpL3Protocol::FindCache (Ptr device) return *i; } } - Ptr ipv4 = m_node->QueryInterface (Ipv4Private::iid); + Ptr ipv4 = m_node->QueryInterface (Ipv4L3Protocol::iid); Ipv4Interface *interface = ipv4->FindInterfaceForDevice (device); ArpCache * cache = new ArpCache (device, interface); NS_ASSERT (device->IsBroadcast ()); @@ -82,10 +84,11 @@ ArpL3Protocol::FindCache (Ptr device) } void -ArpL3Protocol::Receive(Packet& packet, Ptr device) +ArpL3Protocol::Receive(Ptr device, const Packet& p, uint16_t protocol, const Address &from) { ArpCache *cache = FindCache (device); ArpHeader arp; + Packet packet = p; packet.RemoveHeader (arp); NS_DEBUG ("ARP: received "<< (arp.IsRequest ()? "request" : "reply") << @@ -104,7 +107,7 @@ ArpL3Protocol::Receive(Packet& packet, Ptr device) } else if (arp.IsReply () && arp.GetDestinationIpv4Address ().IsEqual (cache->GetInterface ()->GetAddress ()) && - arp.GetDestinationHardwareAddress ().IsEqual (device->GetAddress ())) + arp.GetDestinationHardwareAddress () == device->GetAddress ()) { Ipv4Address from = arp.GetSourceIpv4Address (); ArpCache::Entry *entry = cache->Lookup (from); @@ -115,7 +118,7 @@ ArpL3Protocol::Receive(Packet& packet, Ptr device) NS_DEBUG ("node="<GetId ()<<", got reply from " << arp.GetSourceIpv4Address () << " for waiting entry -- flush"); - MacAddress from_mac = arp.GetSourceHardwareAddress (); + Address from_mac = arp.GetSourceHardwareAddress (); Packet waiting = entry->MarkAlive (from_mac); cache->GetInterface ()->Send (waiting, arp.GetSourceIpv4Address ()); } @@ -144,8 +147,8 @@ ArpL3Protocol::Receive(Packet& packet, Ptr device) } bool ArpL3Protocol::Lookup (Packet &packet, Ipv4Address destination, - Ptr device, - MacAddress *hardwareDestination) + Ptr device, + Address *hardwareDestination) { ArpCache *cache = FindCache (device); ArpCache::Entry *entry = cache->Lookup (destination); @@ -231,7 +234,7 @@ ArpL3Protocol::SendArpRequest (ArpCache const *cache, Ipv4Address to) } void -ArpL3Protocol::SendArpReply (ArpCache const *cache, Ipv4Address toIp, MacAddress toMac) +ArpL3Protocol::SendArpReply (ArpCache const *cache, Ipv4Address toIp, Address toMac) { ArpHeader arp; NS_DEBUG ("ARP: sending reply from node "<GetId ()<< diff --git a/src/internet-node/arp-l3-protocol.h b/src/internet-node/arp-l3-protocol.h index d27d2dba2..a2ea8227e 100644 --- a/src/internet-node/arp-l3-protocol.h +++ b/src/internet-node/arp-l3-protocol.h @@ -23,9 +23,8 @@ #include #include "ns3/ipv4-address.h" -#include "ns3/mac-address.h" +#include "ns3/address.h" #include "ns3/ptr.h" -#include "l3-protocol.h" namespace ns3 { @@ -38,22 +37,23 @@ class TraceContext; /** * \brief An implementation of the ARP protocol */ -class ArpL3Protocol : public L3Protocol +class ArpL3Protocol : public Object { public: + static const InterfaceId iid; static const uint16_t PROT_NUMBER; /** * \brief Constructor * \param node The node which this ARP object is associated with */ ArpL3Protocol (Ptr node); - ~ArpL3Protocol (); + virtual ~ArpL3Protocol (); virtual TraceResolver *CreateTraceResolver (TraceContext const &context); /** * \brief Recieve a packet */ - virtual void Receive(Packet& p, Ptr device); + void Receive(Ptr device, const Packet& p, uint16_t protocol, const Address &from); /** * \brief Perform an ARP lookup * \param p @@ -64,14 +64,14 @@ public: */ bool Lookup (Packet &p, Ipv4Address destination, Ptr device, - MacAddress *hardwareDestination); + Address *hardwareDestination); protected: virtual void DoDispose (void); private: typedef std::list CacheList; ArpCache *FindCache (Ptr device); void SendArpRequest (ArpCache const *cache, Ipv4Address to); - void SendArpReply (ArpCache const *cache, Ipv4Address toIp, MacAddress toMac); + void SendArpReply (ArpCache const *cache, Ipv4Address toIp, Address toMac); CacheList m_cacheList; Ptr m_node; }; diff --git a/src/internet-node/arp-private.cc b/src/internet-node/arp-private.cc deleted file mode 100644 index 10a86b3e3..000000000 --- a/src/internet-node/arp-private.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2007 INRIA - * All rights reserved. - * - * 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: Mathieu Lacage - */ -#include "arp-private.h" -#include "arp-l3-protocol.h" -#include "ns3/assert.h" -#include "ns3/net-device.h" - -namespace ns3 { - -const InterfaceId ArpPrivate::iid = MakeInterfaceId ("ArpPrivate", Object::iid); - -ArpPrivate::ArpPrivate (Ptr arp) - : m_arp (arp) -{ - SetInterfaceId (ArpPrivate::iid); -} -ArpPrivate::~ArpPrivate () -{ - NS_ASSERT (m_arp == 0); -} - -bool -ArpPrivate::Lookup (Packet &p, Ipv4Address destination, - Ptr device, - MacAddress *hardwareDestination) -{ - return m_arp->Lookup (p, destination, device, hardwareDestination); -} - -void -ArpPrivate::DoDispose (void) -{ - m_arp = 0; - Object::DoDispose (); -} - - -} // namespace ns3 diff --git a/src/internet-node/ascii-trace.cc b/src/internet-node/ascii-trace.cc index 1314f2150..961edd709 100644 --- a/src/internet-node/ascii-trace.cc +++ b/src/internet-node/ascii-trace.cc @@ -24,14 +24,8 @@ #include "ns3/trace-root.h" #include "ns3/simulator.h" #include "ns3/node.h" +#include "ns3/packet.h" #include "ns3/queue.h" -#include "ns3/node-list.h" -#include "ns3/llc-snap-header.h" - -#include "ipv4-l3-protocol.h" -#include "arp-header.h" -#include "udp-header.h" -#include "ipv4-header.h" namespace ns3 { @@ -47,90 +41,62 @@ void AsciiTrace::TraceAllQueues (void) { Packet::EnableMetadata (); - TraceRoot::Connect ("/nodes/*/ipv4/interfaces/*/netdevice/queue/*", - MakeCallback (&AsciiTrace::LogDevQueue, this)); + TraceRoot::Connect ("/nodes/*/devices/*/queue/enqueue", + MakeCallback (&AsciiTrace::LogDevQueueEnqueue, this)); + TraceRoot::Connect ("/nodes/*/devices/*/queue/dequeue", + MakeCallback (&AsciiTrace::LogDevQueueDequeue, this)); + TraceRoot::Connect ("/nodes/*/devices/*/queue/drop", + MakeCallback (&AsciiTrace::LogDevQueueDrop, this)); } void AsciiTrace::TraceAllNetDeviceRx (void) { Packet::EnableMetadata (); - TraceRoot::Connect ("/nodes/*/ipv4/interfaces/*/netdevice/rx", + TraceRoot::Connect ("/nodes/*/devices/*/rx", MakeCallback (&AsciiTrace::LogDevRx, this)); } -void -AsciiTrace::PrintType (Packet const &packet) +void +AsciiTrace::LogDevQueueEnqueue (TraceContext const &context, + Packet const &packet) { - Packet p = packet; - LlcSnapHeader llc; - p.RemoveHeader (llc); - switch (llc.GetType ()) - { - case 0x0800: { - Ipv4Header ipv4; - p.RemoveHeader (ipv4); - if (ipv4.GetProtocol () == 17) - { - UdpHeader udp; - p.RemoveHeader (udp); - m_os << "udp size=" << p.GetSize (); - } - } break; - case 0x0806: { - ArpHeader arp; - p.RemoveHeader (arp); - m_os << "arp "; - if (arp.IsRequest ()) - { - m_os << "request"; - } - else - { - m_os << "reply "; - } - } break; - } -} + m_os << "+ "; + m_os << Simulator::Now ().GetSeconds () << " "; + context.Print (m_os); + m_os << " pkt-uid=" << packet.GetUid () << " "; + packet.Print (m_os); + m_os << std::endl; +} void -AsciiTrace::LogDevQueue (TraceContext const &context, Packet const &packet) +AsciiTrace::LogDevQueueDequeue (TraceContext const &context, + Packet const &packet) { - enum Queue::TraceType type; - context.Get (type); - switch (type) - { - case Queue::ENQUEUE: - m_os << "+ "; - break; - case Queue::DEQUEUE: - m_os << "- "; - break; - case Queue::DROP: - m_os << "d "; - break; - } + m_os << "- "; m_os << Simulator::Now ().GetSeconds () << " "; - NodeList::NodeIndex nodeIndex; - context.Get (nodeIndex); - m_os << "node=" << NodeList::GetNode (nodeIndex)->GetId () << " "; - Ipv4L3Protocol::InterfaceIndex interfaceIndex; - context.Get (interfaceIndex); - m_os << "interface=" << interfaceIndex << " "; - m_os << "pkt-uid=" << packet.GetUid () << " "; + context.Print (m_os); + m_os << " pkt-uid=" << packet.GetUid () << " "; + packet.Print (m_os); + m_os << std::endl; +} + +void +AsciiTrace::LogDevQueueDrop (TraceContext const &context, + Packet const &packet) +{ + m_os << "d "; + m_os << Simulator::Now ().GetSeconds () << " "; + context.Print (m_os); + m_os << " pkt-uid=" << packet.GetUid () << " "; packet.Print (m_os); m_os << std::endl; } void -AsciiTrace::LogDevRx (TraceContext const &context, Packet &p) +AsciiTrace::LogDevRx (TraceContext const &context, const Packet &p) { m_os << "r " << Simulator::Now ().GetSeconds () << " "; - NodeList::NodeIndex nodeIndex; - context.Get (nodeIndex); - m_os << "node=" << NodeList::GetNode (nodeIndex)->GetId () << " "; - Ipv4L3Protocol::InterfaceIndex interfaceIndex; - context.Get (interfaceIndex); - m_os << "interface=" << interfaceIndex << " "; - m_os << "pkt-uid=" << p.GetUid () << " "; + context.Print (m_os); + m_os << " pkt-uid=" << p.GetUid () << " "; p.Print (m_os); m_os << std::endl; } diff --git a/src/internet-node/ascii-trace.h b/src/internet-node/ascii-trace.h index 5d7554665..084f82830 100644 --- a/src/internet-node/ascii-trace.h +++ b/src/internet-node/ascii-trace.h @@ -37,9 +37,10 @@ public: void TraceAllQueues (void); void TraceAllNetDeviceRx (void); private: - void PrintType (Packet const &p); - void LogDevQueue (TraceContext const &context, const Packet &p); - void LogDevRx (TraceContext const &context, Packet &p); + void LogDevQueueEnqueue (TraceContext const &context, const Packet &p); + void LogDevQueueDequeue (TraceContext const &context, const Packet &p); + void LogDevQueueDrop (TraceContext const &context, const Packet &p); + void LogDevRx (TraceContext const &context, const Packet &p); std::ofstream m_os; }; diff --git a/src/internet-node/internet-node.cc b/src/internet-node/internet-node.cc index 0400e241f..5c2a3de72 100644 --- a/src/internet-node/internet-node.cc +++ b/src/internet-node/internet-node.cc @@ -23,21 +23,18 @@ #include "ns3/composite-trace-resolver.h" #include "ns3/net-device.h" +#include "ns3/callback.h" -#include "l3-demux.h" #include "ipv4-l4-demux.h" #include "internet-node.h" #include "udp-l4-protocol.h" #include "ipv4-l3-protocol.h" #include "arp-l3-protocol.h" #include "udp-impl.h" -#include "arp-private.h" #include "ipv4-impl.h" -#include "ipv4-private.h" namespace ns3 { - InternetNode::InternetNode() { Construct (); @@ -56,39 +53,34 @@ InternetNode::Construct (void) { Ptr ipv4 = Create (this); Ptr arp = Create (this); - Ptr udp = Create (this); + // XXX remove the PeekPointer below. + RegisterProtocolHandler (MakeCallback (&Ipv4L3Protocol::Receive, PeekPointer (ipv4)), + Ipv4L3Protocol::PROT_NUMBER, 0); + RegisterProtocolHandler (MakeCallback (&ArpL3Protocol::Receive, PeekPointer (arp)), + ArpL3Protocol::PROT_NUMBER, 0); + - Ptr l3Demux = Create (this); Ptr ipv4L4Demux = Create (this); - - l3Demux->Insert (ipv4); - l3Demux->Insert (arp); + Ptr udp = Create (this); ipv4L4Demux->Insert (udp); Ptr udpImpl = Create (udp); - Ptr arpPrivate = Create (arp); Ptr ipv4Impl = Create (ipv4); - Ptr ipv4Private = Create (ipv4); - Object::AddInterface (ipv4Private); + Object::AddInterface (ipv4); + Object::AddInterface (arp); Object::AddInterface (ipv4Impl); - Object::AddInterface (arpPrivate); Object::AddInterface (udpImpl); - Object::AddInterface (l3Demux); Object::AddInterface (ipv4L4Demux); } - -TraceResolver * -InternetNode::DoCreateTraceResolver (TraceContext const &context) +void +InternetNode::DoFillTraceResolver (CompositeTraceResolver &resolver) { - CompositeTraceResolver *resolver = new CompositeTraceResolver (context); - Ptr ipv4 = QueryInterface (Ipv4Private::iid); - resolver->Add ("ipv4", - MakeCallback (&Ipv4Private::CreateTraceResolver, PeekPointer (ipv4)), - InternetNode::IPV4); - - return resolver; + Node::DoFillTraceResolver (resolver); + Ptr ipv4 = QueryInterface (Ipv4L3Protocol::iid); + resolver.Add ("ipv4", + MakeCallback (&Ipv4L3Protocol::CreateTraceResolver, PeekPointer (ipv4))); } void @@ -97,25 +89,4 @@ InternetNode::DoDispose() Node::DoDispose (); } -void -InternetNode::DoAddDevice (Ptr device) -{ - device->SetReceiveCallback (MakeCallback (&InternetNode::ReceiveFromDevice, this)); -} - -bool -InternetNode::ReceiveFromDevice (Ptr device, const Packet &p, uint16_t protocolNumber) const -{ - Ptr demux = QueryInterface (L3Demux::iid); - Ptr target = demux->GetProtocol (protocolNumber); - if (target != 0) - { - Packet packet = p; - target->Receive(packet, device); - return true; - } - return false; -} - - }//namespace ns3 diff --git a/src/internet-node/internet-node.h b/src/internet-node/internet-node.h index b577f6b6b..deabd20eb 100644 --- a/src/internet-node/internet-node.h +++ b/src/internet-node/internet-node.h @@ -36,9 +36,6 @@ class Packet; class InternetNode : public Node { public: - enum TraceType { - IPV4, - }; InternetNode(); InternetNode(uint32_t systemId); virtual ~InternetNode (); @@ -46,8 +43,7 @@ public: protected: virtual void DoDispose(void); private: - virtual void DoAddDevice (Ptr device); - virtual TraceResolver *DoCreateTraceResolver (TraceContext const &context); + virtual void DoFillTraceResolver (CompositeTraceResolver &resolver); bool ReceiveFromDevice (Ptr device, const Packet &p, uint16_t protocolNumber) const; void Construct (void); }; diff --git a/src/internet-node/ipv4-header.cc b/src/internet-node/ipv4-header.cc index 44bc46fd2..83750632d 100644 --- a/src/internet-node/ipv4-header.cc +++ b/src/internet-node/ipv4-header.cc @@ -28,8 +28,17 @@ NS_DEBUG_COMPONENT_DEFINE ("Ipv4Header"); namespace ns3 { +NS_HEADER_ENSURE_REGISTERED (Ipv4Header); + bool Ipv4Header::m_calcChecksum = false; +uint32_t +Ipv4Header::GetUid (void) +{ + static uint32_t uid = AllocateUid ("Ipv4Header.ns3"); + return uid; +} + Ipv4Header::Ipv4Header () : m_payloadSize (0), m_identification (0), @@ -40,8 +49,6 @@ Ipv4Header::Ipv4Header () m_fragmentOffset (0), m_goodChecksum (true) {} -Ipv4Header::~Ipv4Header () -{} void Ipv4Header::EnableChecksums (void) @@ -180,13 +187,13 @@ Ipv4Header::IsChecksumOk (void) const } std::string -Ipv4Header::DoGetName (void) const +Ipv4Header::GetName (void) const { return "IPV4"; } void -Ipv4Header::PrintTo (std::ostream &os) const +Ipv4Header::Print (std::ostream &os) const { // ipv4, right ? std::string flags; @@ -229,7 +236,7 @@ Ipv4Header::GetSerializedSize (void) const } void -Ipv4Header::SerializeTo (Buffer::Iterator start) const +Ipv4Header::Serialize (Buffer::Iterator start) const { Buffer::Iterator i = start; @@ -272,7 +279,7 @@ Ipv4Header::SerializeTo (Buffer::Iterator start) const } } uint32_t -Ipv4Header::DeserializeFrom (Buffer::Iterator start) +Ipv4Header::Deserialize (Buffer::Iterator start) { Buffer::Iterator i = start; uint8_t verIhl = i.ReadU8 (); diff --git a/src/internet-node/ipv4-header.h b/src/internet-node/ipv4-header.h index b45ed9f02..fe9317d7c 100644 --- a/src/internet-node/ipv4-header.h +++ b/src/internet-node/ipv4-header.h @@ -29,13 +29,14 @@ namespace ns3 { /** * \brief Packet header for IPv4 */ -class Ipv4Header : public Header { +class Ipv4Header : public Header +{ public: + static uint32_t GetUid (void); /** * \brief Construct a null IPv4 header */ Ipv4Header (); - virtual ~Ipv4Header (); /** * \brief Enable checksum calculation for IP (XXX currently has no effect) */ @@ -139,12 +140,12 @@ public: */ bool IsChecksumOk (void) const; + std::string GetName (void) const; + void Print (std::ostream &os) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator start) const; + uint32_t Deserialize (Buffer::Iterator start); private: - virtual std::string DoGetName (void) const; - virtual void PrintTo (std::ostream &os) const; - virtual uint32_t GetSerializedSize (void) const; - virtual void SerializeTo (Buffer::Iterator start) const; - virtual uint32_t DeserializeFrom (Buffer::Iterator start); enum FlagsE { DONT_FRAGMENT = (1<<0), @@ -165,7 +166,7 @@ private: bool m_goodChecksum; }; -}; // namespace ns3 +} // namespace ns3 #endif /* IPV4_HEADER_H */ diff --git a/src/internet-node/ipv4-l3-protocol.cc b/src/internet-node/ipv4-l3-protocol.cc index 077acb3f7..be483dfa0 100644 --- a/src/internet-node/ipv4-l3-protocol.cc +++ b/src/internet-node/ipv4-l3-protocol.cc @@ -41,15 +41,86 @@ NS_DEBUG_COMPONENT_DEFINE ("Ipv4L3Protocol"); namespace ns3 { +const InterfaceId Ipv4L3Protocol::iid = MakeInterfaceId ("Ipv4L3Protocol", Object::iid); const uint16_t Ipv4L3Protocol::PROT_NUMBER = 0x0800; +Ipv4L3ProtocolTraceContextElement::Ipv4L3ProtocolTraceContextElement () + : m_type (TX) +{} +Ipv4L3ProtocolTraceContextElement::Ipv4L3ProtocolTraceContextElement (enum Type type) + : m_type (type) +{} +bool +Ipv4L3ProtocolTraceContextElement::IsTx (void) const +{ + return m_type == TX; +} +bool +Ipv4L3ProtocolTraceContextElement::IsRx (void) const +{ + return m_type == RX; +} +bool +Ipv4L3ProtocolTraceContextElement::IsDrop (void) const +{ + return m_type == DROP; +} +void +Ipv4L3ProtocolTraceContextElement::Print (std::ostream &os) const +{ + os << "ipv4="; + switch (m_type) + { + case TX: + os << "tx"; + break; + case RX: + os << "rx"; + break; + case DROP: + os << "drop"; + break; + } +} +uint16_t +Ipv4L3ProtocolTraceContextElement::GetUid (void) +{ + static uint16_t uid = AllocateUid ("Ipv4L3ProtocolTraceContextElement"); + return uid; +} + + +Ipv4l3ProtocolInterfaceIndex::Ipv4l3ProtocolInterfaceIndex () + : m_index (0) +{} +Ipv4l3ProtocolInterfaceIndex::Ipv4l3ProtocolInterfaceIndex (uint32_t index) + : m_index (index) +{} +uint32_t +Ipv4l3ProtocolInterfaceIndex::Get (void) const +{ + return m_index; +} +void +Ipv4l3ProtocolInterfaceIndex::Print (std::ostream &os) const +{ + os << "ipv4-interface=" << m_index; +} +uint16_t +Ipv4l3ProtocolInterfaceIndex::GetUid (void) +{ + static uint16_t uid = AllocateUid ("Ipv4l3ProtocolInterfaceIndex"); + return uid; +} + + Ipv4L3Protocol::Ipv4L3Protocol(Ptr node) - : L3Protocol (PROT_NUMBER, 4), - m_nInterfaces (0), + : m_nInterfaces (0), m_defaultTtl (64), m_identification (0), m_node (node) { + SetInterfaceId (Ipv4L3Protocol::iid); m_staticRouting = Create (); AddRoutingProtocol (m_staticRouting, 0); SetupLoopback (); @@ -66,9 +137,9 @@ Ipv4L3Protocol::DoDispose (void) } m_interfaces.clear (); m_node = 0; - L3Protocol::DoDispose (); m_staticRouting->Dispose (); m_staticRouting = 0; + Object::DoDispose (); } void @@ -86,20 +157,19 @@ TraceResolver * Ipv4L3Protocol::CreateTraceResolver (TraceContext const &context) { CompositeTraceResolver *resolver = new CompositeTraceResolver (context); - resolver->Add ("tx", m_txTrace, Ipv4L3Protocol::TX); - resolver->Add ("rx", m_rxTrace, Ipv4L3Protocol::RX); - resolver->Add ("drop", m_dropTrace, Ipv4L3Protocol::DROP); + resolver->Add ("tx", m_txTrace, Ipv4L3ProtocolTraceContextElement(Ipv4L3ProtocolTraceContextElement::TX)); + resolver->Add ("rx", m_rxTrace, Ipv4L3ProtocolTraceContextElement(Ipv4L3ProtocolTraceContextElement::RX)); + resolver->Add ("drop", m_dropTrace, Ipv4L3ProtocolTraceContextElement (Ipv4L3ProtocolTraceContextElement::DROP)); resolver->Add ("interfaces", - MakeCallback (&Ipv4L3Protocol::InterfacesCreateTraceResolver, this), - Ipv4L3Protocol::INTERFACES); + MakeCallback (&Ipv4L3Protocol::InterfacesCreateTraceResolver, this)); return resolver; } TraceResolver * Ipv4L3Protocol::InterfacesCreateTraceResolver (TraceContext const &context) const { - ArrayTraceResolver *resolver = - new ArrayTraceResolver + ArrayTraceResolver *resolver = + new ArrayTraceResolver (context, MakeCallback (&Ipv4L3Protocol::GetNInterfaces, this), MakeCallback (&Ipv4L3Protocol::GetInterface, this)); @@ -240,18 +310,19 @@ Ipv4L3Protocol::FindInterfaceForDevice (Ptr device) } void -Ipv4L3Protocol::Receive(Packet& packet, Ptr device) +Ipv4L3Protocol::Receive( Ptr device, const Packet& p, uint16_t protocol, const Address &from) { uint32_t index = 0; for (Ipv4InterfaceList::const_iterator i = m_interfaces.begin (); i != m_interfaces.end (); i++) { if ((*i)->GetDevice () == device) { - m_rxTrace (packet, index); + m_rxTrace (p, index); break; } index++; } + Packet packet = p; Ipv4Header ipHeader; packet.RemoveHeader (ipHeader); diff --git a/src/internet-node/ipv4-l3-protocol.h b/src/internet-node/ipv4-l3-protocol.h index b41b6aefa..2cce3efa3 100644 --- a/src/internet-node/ipv4-l3-protocol.h +++ b/src/internet-node/ipv4-l3-protocol.h @@ -25,12 +25,11 @@ #include #include #include "ns3/callback-trace-source.h" -#include "ns3/array-trace-resolver.h" +#include "ns3/trace-context-element.h" #include "ns3/ipv4-address.h" -#include "ipv4-header.h" #include "ns3/ptr.h" #include "ns3/ipv4.h" -#include "l3-protocol.h" +#include "ipv4-header.h" #include "ipv4-static-routing.h" namespace ns3 { @@ -45,19 +44,43 @@ class Node; class TraceResolver; class TraceContext; - -class Ipv4L3Protocol : public L3Protocol +class Ipv4L3ProtocolTraceContextElement : public TraceContextElement { public: - static const uint16_t PROT_NUMBER; - - enum TraceType { + enum Type { TX, RX, DROP, - INTERFACES, }; - typedef ArrayTraceResolver::Index InterfaceIndex; + Ipv4L3ProtocolTraceContextElement (); + Ipv4L3ProtocolTraceContextElement (enum Type type); + bool IsTx (void) const; + bool IsRx (void) const; + bool IsDrop (void) const; + void Print (std::ostream &os) const; + static uint16_t GetUid (void); +private: + enum Type m_type; +}; + +class Ipv4l3ProtocolInterfaceIndex : public TraceContextElement +{ +public: + Ipv4l3ProtocolInterfaceIndex (); + Ipv4l3ProtocolInterfaceIndex (uint32_t index); + uint32_t Get (void) const; + void Print (std::ostream &os) const; + static uint16_t GetUid (void); +private: + uint32_t m_index; +}; + + +class Ipv4L3Protocol : public Object +{ +public: + static const InterfaceId iid; + static const uint16_t PROT_NUMBER; Ipv4L3Protocol(Ptr node); virtual ~Ipv4L3Protocol (); @@ -95,7 +118,7 @@ public: * - implement a per-NetDevice ARP cache * - send back arp replies on the right device */ - virtual void Receive(Packet& p, Ptr device); + void Receive( Ptr device, const Packet& p, uint16_t protocol, const Address &from); /** * \param packet packet to send diff --git a/src/internet-node/ipv4-l4-demux.cc b/src/internet-node/ipv4-l4-demux.cc index d39e922df..977b802ba 100644 --- a/src/internet-node/ipv4-l4-demux.cc +++ b/src/internet-node/ipv4-l4-demux.cc @@ -32,6 +32,30 @@ namespace ns3 { const InterfaceId Ipv4L4Demux::iid = MakeInterfaceId ("Ipv4L4Demux", Object::iid); +Ipv4L4ProtocolTraceContextElement::Ipv4L4ProtocolTraceContextElement () + : m_protocolNumber (0) +{} +Ipv4L4ProtocolTraceContextElement::Ipv4L4ProtocolTraceContextElement (int protocolNumber) + : m_protocolNumber (protocolNumber) +{} +int +Ipv4L4ProtocolTraceContextElement::Get (void) const +{ + return m_protocolNumber; +} +void +Ipv4L4ProtocolTraceContextElement::Print (std::ostream &os) const +{ + os << "ipv4-protocol=0x" << std::hex << m_protocolNumber << std::dec; +} +uint16_t +Ipv4L4ProtocolTraceContextElement::GetUid (void) +{ + static uint16_t uid = AllocateUid ("Ipv4L4ProtocolTraceContextElement"); + return uid; +} + + Ipv4L4Demux::Ipv4L4Demux (Ptr node) : m_node (node) { @@ -64,7 +88,7 @@ Ipv4L4Demux::CreateTraceResolver (TraceContext const &context) std::string protValue; std::ostringstream oss (protValue); oss << (*i)->GetProtocolNumber (); - Ipv4L4ProtocolTraceType protocolNumber = (*i)->GetProtocolNumber (); + Ipv4L4ProtocolTraceContextElement protocolNumber = (*i)->GetProtocolNumber (); resolver->Add (protValue, MakeCallback (&Ipv4L4Protocol::CreateTraceResolver, PeekPointer (protocol)), protocolNumber); diff --git a/src/internet-node/ipv4-l4-demux.h b/src/internet-node/ipv4-l4-demux.h index f511fcd50..7e7fba62e 100644 --- a/src/internet-node/ipv4-l4-demux.h +++ b/src/internet-node/ipv4-l4-demux.h @@ -28,6 +28,7 @@ #include #include "ns3/object.h" #include "ns3/ptr.h" +#include "ns3/trace-context-element.h" namespace ns3 { @@ -36,6 +37,18 @@ class Node; class TraceResolver; class TraceContext; +class Ipv4L4ProtocolTraceContextElement : public TraceContextElement +{ +public: + Ipv4L4ProtocolTraceContextElement (); + Ipv4L4ProtocolTraceContextElement (int protocolNumber); + int Get (void) const; + void Print (std::ostream &os) const; + static uint16_t GetUid (void); +private: + int m_protocolNumber; +}; + /** * \brief L4 Ipv4 Demux */ @@ -43,7 +56,6 @@ class Ipv4L4Demux : public Object { public: static const InterfaceId iid; - typedef int Ipv4L4ProtocolTraceType; Ipv4L4Demux (Ptr node); virtual ~Ipv4L4Demux(); diff --git a/src/internet-node/ipv4-loopback-interface.cc b/src/internet-node/ipv4-loopback-interface.cc index 906c0a001..be680a022 100644 --- a/src/internet-node/ipv4-loopback-interface.cc +++ b/src/internet-node/ipv4-loopback-interface.cc @@ -22,8 +22,9 @@ #include "ns3/empty-trace-resolver.h" #include "ns3/net-device.h" #include "ns3/node.h" +#include "ns3/eui48-address.h" #include "ipv4-loopback-interface.h" -#include "ipv4-private.h" +#include "ipv4-l3-protocol.h" namespace ns3 { @@ -43,8 +44,8 @@ Ipv4LoopbackInterface::DoCreateTraceResolver (TraceContext const &context) void Ipv4LoopbackInterface::SendTo (Packet packet, Ipv4Address dest) { - Ptr ipv4 = m_node->QueryInterface (Ipv4Private::iid); - ipv4->Receive (packet, GetDevice ()); + Ptr ipv4 = m_node->QueryInterface (Ipv4L3Protocol::iid); + ipv4->Receive (GetDevice (), packet, Ipv4L3Protocol::PROT_NUMBER, Eui48Address ("ff:ff:ff:ff:ff:ff")); } }//namespace ns3 diff --git a/src/internet-node/ipv4-private.cc b/src/internet-node/ipv4-private.cc deleted file mode 100644 index 1b9314152..000000000 --- a/src/internet-node/ipv4-private.cc +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2007 INRIA - * All rights reserved. - * - * 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: Mathieu Lacage - */ -#include "ipv4-private.h" -#include "ipv4-l3-protocol.h" -#include "ns3/assert.h" -#include "ns3/net-device.h" - -namespace ns3 { - -const InterfaceId Ipv4Private::iid = MakeInterfaceId ("Ipv4Private", Object::iid); - -Ipv4Private::Ipv4Private (Ptr ipv4) - : m_ipv4 (ipv4) -{ - SetInterfaceId (Ipv4Private::iid); -} -Ipv4Private::~Ipv4Private () -{ - NS_ASSERT (m_ipv4 == 0); -} -TraceResolver * -Ipv4Private::CreateTraceResolver (TraceContext const &context) -{ - return m_ipv4->CreateTraceResolver (context); -} -void -Ipv4Private::Send (Packet const &packet, Ipv4Address source, - Ipv4Address destination, uint8_t protocol) -{ - m_ipv4->Send (packet, source, destination, protocol); -} -Ipv4Interface * -Ipv4Private::FindInterfaceForDevice (Ptrdevice) -{ - return m_ipv4->FindInterfaceForDevice (device); -} -void -Ipv4Private::Receive(Packet& p, Ptr device) -{ - m_ipv4->Receive (p, device); -} -void -Ipv4Private::DoDispose (void) -{ - m_ipv4 = 0; - Object::DoDispose (); -} - -} // namespace ns3 diff --git a/src/internet-node/ipv4-private.h b/src/internet-node/ipv4-private.h deleted file mode 100644 index 3208d0f1a..000000000 --- a/src/internet-node/ipv4-private.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2007 INRIA - * All rights reserved. - * - * 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: Mathieu Lacage - */ -#ifndef IPV4_PRIVATE_H -#define IPV4_PRIVATE_H - -#include "ns3/object.h" -#include "ns3/ipv4-address.h" -#include "ns3/ptr.h" -#include - -namespace ns3 { - -class Packet; -class Ipv4L3Protocol; -class TraceContext; -class TraceResolver; -class Ipv4Interface; -class NetDevice; - -class Ipv4Private : public Object -{ -public: - static const InterfaceId iid; - Ipv4Private (Ptr ipv4); - virtual ~Ipv4Private (); - - TraceResolver *CreateTraceResolver (TraceContext const &context); - void Send (Packet const &packet, Ipv4Address source, - Ipv4Address destination, uint8_t protocol); - Ipv4Interface *FindInterfaceForDevice (Ptrdevice); - void Receive(Packet& p, Ptr device); -protected: - virtual void DoDispose (void); -private: - Ptr m_ipv4; -}; - -} // namespace ns3 - -#endif /* IPV4_PRIVATE_H */ diff --git a/src/internet-node/ipv4-static-routing.h b/src/internet-node/ipv4-static-routing.h index f7e6d52ab..8462f55a7 100644 --- a/src/internet-node/ipv4-static-routing.h +++ b/src/internet-node/ipv4-static-routing.h @@ -31,7 +31,6 @@ #include "ipv4-header.h" #include "ns3/ptr.h" #include "ns3/ipv4.h" -#include "l3-protocol.h" namespace ns3 { diff --git a/src/internet-node/l3-demux.cc b/src/internet-node/l3-demux.cc deleted file mode 100644 index 6e8416a50..000000000 --- a/src/internet-node/l3-demux.cc +++ /dev/null @@ -1,90 +0,0 @@ -// -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- -// -// Copyright (c) 2006 Georgia Tech Research Corporation -// All rights reserved. -// -// 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: George F. Riley -// -// Implement the L3Protocols capability for ns3. -// George F. Riley, Georgia Tech, Fall 2006 -#include -#include -#include "ns3/composite-trace-resolver.h" -#include "ns3/node.h" -#include "l3-demux.h" -#include "l3-protocol.h" - -namespace ns3 { - -const InterfaceId L3Demux::iid = MakeInterfaceId ("L3Demux", Object::iid); - -L3Demux::L3Demux (Ptr node) - : m_node (node) -{ - SetInterfaceId (L3Demux::iid); -} - -L3Demux::~L3Demux() -{} - -void -L3Demux::DoDispose (void) -{ - for (L3Map_t::iterator i = m_protocols.begin(); i != m_protocols.end(); ++i) - { - i->second->Dispose (); - i->second = 0; - } - m_protocols.clear (); - m_node = 0; - Object::DoDispose (); -} - -TraceResolver * -L3Demux::CreateTraceResolver (TraceContext const &context) const -{ - CompositeTraceResolver *resolver = new CompositeTraceResolver (context); - for (L3Map_t::const_iterator i = m_protocols.begin(); i != m_protocols.end(); ++i) - { - std::string protValue; - std::ostringstream oss (protValue); - oss << i->second->GetProtocolNumber (); - ProtocolTraceType context = i->second->GetProtocolNumber (); - resolver->Add (protValue, - MakeCallback (&L3Protocol::CreateTraceResolver, PeekPointer (i->second)), - context); - } - return resolver; -} - -void L3Demux::Insert(Ptr p) -{ - m_protocols.insert(L3Map_t::value_type(p->GetProtocolNumber (), p)); -} - -Ptr -L3Demux::GetProtocol (int p) -{ // Look up a protocol by protocol number - L3Map_t::iterator i = m_protocols.find(p); - if (i == m_protocols.end()) - { - return 0; - } - return i->second; // Return the protocol -} - -} //namespace ns3 - diff --git a/src/internet-node/l3-demux.h b/src/internet-node/l3-demux.h deleted file mode 100644 index 43a3e1b7d..000000000 --- a/src/internet-node/l3-demux.h +++ /dev/null @@ -1,93 +0,0 @@ -// -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- -// -// Copyright (c) 2006 Georgia Tech Research Corporation -// All rights reserved. -// -// 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: George F. Riley -// -// Define the L3Protocols capability for ns3. -// George F. Riley, Georgia Tech, Fall 2006 - -// This object manages the different layer 3 protocols for any ns3 -// node that has this capability. - -#ifndef L3_DEMUX_H -#define L3_DEMUX_H - -#include -#include "ns3/object.h" -#include "ns3/ptr.h" - -namespace ns3 { - -class L3Protocol; -class Node; -class TraceResolver; -class TraceContext; - -/** - * \brief L3 Demux - */ -class L3Demux : public Object -{ -public: - static const InterfaceId iid; - typedef int ProtocolTraceType; - L3Demux(Ptr node); - virtual ~L3Demux(); - - /** - * \param context the trace context to use to construct the - * TraceResolver to return - * \returns a TraceResolver which can resolve all traces - * performed in this object. The caller must - * delete the returned object. - */ - TraceResolver *CreateTraceResolver (TraceContext const &context) const; - - - /** - * \param protocol a template for the protocol to add to this L3 Demux. - * - * Invoke Copy on the input template to get a copy of the input - * protocol which can be used on the Node on which this L3 Demux - * is running. The new L3Protocol is registered internally as - * a working L3 Protocol and returned from this method. - * The caller does not get ownership of the returned pointer. - */ - void Insert(Ptr protocol); - /** - * \param protocolNumber number of protocol to lookup - * in this L4 Demux - * \returns a matching L3 Protocol - * - * This method is typically called by lower layers - * to forward packets up the stack to the right protocol. - * It is also called from NodeImpl::GetIpv4 for example. - */ - Ptr GetProtocol (int protocolNumber); -protected: - virtual void DoDispose (void); -private: - typedef std::map > L3Map_t; - - Ptr m_node; - L3Map_t m_protocols; -}; - -} //namespace ns3 -#endif - diff --git a/src/internet-node/l3-protocol.cc b/src/internet-node/l3-protocol.cc deleted file mode 100644 index 71dbc2d46..000000000 --- a/src/internet-node/l3-protocol.cc +++ /dev/null @@ -1,54 +0,0 @@ -// -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- -// -// Copyright (c) 2006 Georgia Tech Research Corporation -// All rights reserved. -// -// 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: George F. Riley -// - -// NS3 - Layer 3 Protocol base class -// George F. Riley, Georgia Tech, Spring 2007 - -#include "l3-protocol.h" - - -namespace ns3 { - -L3Protocol::L3Protocol(int protocolNumber, int version) - : m_protocolNumber (protocolNumber), - m_version (version) -{} -L3Protocol::~L3Protocol () -{} - -int -L3Protocol::GetProtocolNumber (void) const -{ - return m_protocolNumber; -} -int -L3Protocol::GetVersion() const -{ - return m_version; -} - -void -L3Protocol::DoDispose (void) -{ - Object::DoDispose (); -} - -}//namespace ns3 diff --git a/src/internet-node/l3-protocol.h b/src/internet-node/l3-protocol.h deleted file mode 100644 index e0c2a469a..000000000 --- a/src/internet-node/l3-protocol.h +++ /dev/null @@ -1,73 +0,0 @@ -// -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- -// -// Copyright (c) 2006 Georgia Tech Research Corporation -// All rights reserved. -// -// 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: George F. Riley -// - -// NS3 - Layer 3 Protocol base class -// George F. Riley, Georgia Tech, Spring 2007 - -#ifndef L3_PROTOCOL_H -#define L3_PROTOCOL_H - -#include "ns3/object.h" -#include "ns3/ptr.h" - -namespace ns3 { - -class Packet; -class NetDevice; -class TraceResolver; -class TraceContext; - -/** - * ::Send is always defined in subclasses. - */ -class L3Protocol : public Object { -public: - L3Protocol(int protocolNumber, int version); - virtual ~L3Protocol (); - /** - * \return The protocol number of this Layer 3 protocol - */ - int GetProtocolNumber (void) const; - /** - * \return The version number of this protocol - */ - int GetVersion() const; - - virtual TraceResolver *CreateTraceResolver (TraceContext const &context) = 0; - /** - * Lower layer calls this method after calling L3Demux::Lookup - * The ARP subclass needs to know from which NetDevice this - * packet is coming to: - * - implement a per-NetDevice ARP cache - * - send back arp replies on the right device - */ - virtual void Receive(Packet& p, Ptr device) = 0; - -protected: - virtual void DoDispose (void); -private: - int m_protocolNumber; - int m_version; -}; - -} // Namespace ns3 - -#endif diff --git a/src/internet-node/pcap-trace.cc b/src/internet-node/pcap-trace.cc index 6fa329d4d..240100fb5 100644 --- a/src/internet-node/pcap-trace.cc +++ b/src/internet-node/pcap-trace.cc @@ -82,10 +82,9 @@ PcapTrace::GetStream (uint32_t nodeId, uint32_t interfaceId) void PcapTrace::LogIp (TraceContext const &context, Packet const &p, uint32_t interfaceIndex) { - NodeList::NodeIndex nodeIndex; + NodeListIndex nodeIndex; context.Get (nodeIndex); - uint32_t nodeId = NodeList::GetNode (nodeIndex)->GetId (); - PcapWriter *writer = GetStream (nodeId, interfaceIndex); + PcapWriter *writer = GetStream (nodeIndex.Get (), interfaceIndex); writer->WritePacket (p); } diff --git a/src/internet-node/udp-header.cc b/src/internet-node/udp-header.cc index a7a95ca49..96b57beba 100644 --- a/src/internet-node/udp-header.cc +++ b/src/internet-node/udp-header.cc @@ -24,8 +24,17 @@ namespace ns3 { +NS_HEADER_ENSURE_REGISTERED (UdpHeader); + bool UdpHeader::m_calcChecksum = false; +uint32_t +UdpHeader::GetUid (void) +{ + static uint32_t uid = AllocateUid ("UdpHeader.ns3"); + return uid; +} + /* The magic values below are used only for debugging. * They can be used to easily detect memory corruption * problems so you can see the patterns in memory. @@ -84,7 +93,7 @@ UdpHeader::InitializeChecksum (Ipv4Address source, destination.Serialize (buf+4); buf[8] = 0; buf[9] = protocol; - uint16_t udpLength = m_payloadSize + GetSize (); + uint16_t udpLength = m_payloadSize + GetSerializedSize (); buf[10] = udpLength >> 8; buf[11] = udpLength & 0xff; @@ -92,16 +101,16 @@ UdpHeader::InitializeChecksum (Ipv4Address source, } std::string -UdpHeader::DoGetName (void) const +UdpHeader::GetName (void) const { return "UDP"; } void -UdpHeader::PrintTo (std::ostream &os) const +UdpHeader::Print (std::ostream &os) const { os << "(" - << "length: " << m_payloadSize + GetSize () + << "length: " << m_payloadSize + GetSerializedSize () << ") " << m_sourcePort << " > " << m_destinationPort ; @@ -114,12 +123,12 @@ UdpHeader::GetSerializedSize (void) const } void -UdpHeader::SerializeTo (Buffer::Iterator start) const +UdpHeader::Serialize (Buffer::Iterator start) const { Buffer::Iterator i = start; i.WriteHtonU16 (m_sourcePort); i.WriteHtonU16 (m_destinationPort); - i.WriteHtonU16 (m_payloadSize + GetSize ()); + i.WriteHtonU16 (m_payloadSize + GetSerializedSize ()); i.WriteU16 (0); if (m_calcChecksum) @@ -128,7 +137,7 @@ UdpHeader::SerializeTo (Buffer::Iterator start) const //XXXX uint16_t checksum = Ipv4ChecksumCalculate (m_initialChecksum, buffer->PeekData (), - GetSize () + m_payloadSize); + GetSerializedSize () + m_payloadSize); checksum = Ipv4ChecksumComplete (checksum); i = buffer->Begin (); i.Next (6); @@ -137,12 +146,12 @@ UdpHeader::SerializeTo (Buffer::Iterator start) const } } uint32_t -UdpHeader::DeserializeFrom (Buffer::Iterator start) +UdpHeader::Deserialize (Buffer::Iterator start) { Buffer::Iterator i = start; m_sourcePort = i.ReadNtohU16 (); m_destinationPort = i.ReadNtohU16 (); - m_payloadSize = i.ReadNtohU16 () - GetSize (); + m_payloadSize = i.ReadNtohU16 () - GetSerializedSize (); if (m_calcChecksum) { // XXX verify checksum. diff --git a/src/internet-node/udp-header.h b/src/internet-node/udp-header.h index 332614ba0..70503ef91 100644 --- a/src/internet-node/udp-header.h +++ b/src/internet-node/udp-header.h @@ -23,6 +23,7 @@ #define UDP_HEADER_H #include +#include #include "ns3/header.h" #include "ns3/ipv4-address.h" @@ -30,15 +31,18 @@ namespace ns3 { /** * \brief Packet header for UDP packets */ -class UdpHeader : public Header { +class UdpHeader : public Header +{ public: + static uint32_t GetUid (void); + /** * \brief Constructor * * Creates a null header */ UdpHeader (); - virtual ~UdpHeader (); + ~UdpHeader (); /** * \brief Enable checksum calculation for UDP (XXX currently has no effect) @@ -80,13 +84,13 @@ public: Ipv4Address destination, uint8_t protocol); -private: - virtual std::string DoGetName (void) const; - virtual void PrintTo (std::ostream &os) const; - virtual uint32_t GetSerializedSize (void) const; - virtual void SerializeTo (Buffer::Iterator start) const; - virtual uint32_t DeserializeFrom (Buffer::Iterator start); + std::string GetName (void) const; + void Print (std::ostream &os) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator start) const; + uint32_t Deserialize (Buffer::Iterator start); +private: uint16_t m_sourcePort; uint16_t m_destinationPort; uint16_t m_payloadSize; @@ -95,6 +99,6 @@ private: static bool m_calcChecksum; }; -}; // namespace ns3 +} // namespace ns3 #endif /* UDP_HEADER */ diff --git a/src/internet-node/udp-l4-protocol.cc b/src/internet-node/udp-l4-protocol.cc index 90fb782b9..f0ef8569b 100644 --- a/src/internet-node/udp-l4-protocol.cc +++ b/src/internet-node/udp-l4-protocol.cc @@ -29,8 +29,6 @@ #include "ipv4-end-point-demux.h" #include "ipv4-end-point.h" #include "ipv4-l3-protocol.h" -#include "ipv4-private.h" -#include "l3-demux.h" #include "udp-socket.h" namespace ns3 { @@ -138,7 +136,7 @@ UdpL4Protocol::Send (Packet packet, packet.AddHeader (udpHeader); - Ptr ipv4 = m_node->QueryInterface (Ipv4Private::iid); + Ptr ipv4 = m_node->QueryInterface (Ipv4L3Protocol::iid); if (ipv4 != 0) { ipv4->Send (packet, saddr, daddr, PROT_NUMBER); diff --git a/src/internet-node/udp-socket.cc b/src/internet-node/udp-socket.cc index 0ab911c9e..a2bde3224 100644 --- a/src/internet-node/udp-socket.cc +++ b/src/internet-node/udp-socket.cc @@ -19,6 +19,7 @@ * Author: Mathieu Lacage */ #include "ns3/node.h" +#include "ns3/inet-socket-address.h" #include "udp-socket.h" #include "udp-l4-protocol.h" #include "ipv4-end-point.h" @@ -56,6 +57,12 @@ UdpSocket::~UdpSocket () m_udp = 0; } +enum Socket::SocketErrno +UdpSocket::GetErrno (void) const +{ + return m_errno; +} + Ptr UdpSocket::GetNode (void) const { @@ -72,12 +79,12 @@ UdpSocket::Destroy (void) int UdpSocket::FinishBind (void) { - m_endPoint->SetRxCallback (MakeCallback (&UdpSocket::ForwardUp, this)); - m_endPoint->SetDestroyCallback (MakeCallback (&UdpSocket::Destroy, this)); if (m_endPoint == 0) { return -1; } + m_endPoint->SetRxCallback (MakeCallback (&UdpSocket::ForwardUp, this)); + m_endPoint->SetDestroyCallback (MakeCallback (&UdpSocket::Destroy, this)); return 0; } @@ -88,29 +95,35 @@ UdpSocket::Bind (void) return FinishBind (); } int -UdpSocket::Bind (Ipv4Address address) +UdpSocket::Bind (const Address &address) { - m_endPoint = m_udp->Allocate (address); - return FinishBind (); -} -int -UdpSocket::Bind (uint16_t port) -{ - m_endPoint = m_udp->Allocate (port); - return FinishBind (); -} -int -UdpSocket::Bind (Ipv4Address address, uint16_t port) -{ - m_endPoint = m_udp->Allocate (address, port); + if (!InetSocketAddress::IsMatchingType (address)) + { + return ERROR_INVAL; + } + InetSocketAddress transport = InetSocketAddress::ConvertFrom (address); + Ipv4Address ipv4 = transport.GetIpv4 (); + uint16_t port = transport.GetPort (); + if (ipv4 == Ipv4Address::GetAny () && port == 0) + { + m_endPoint = m_udp->Allocate (); + } + else if (ipv4 == Ipv4Address::GetAny () && port != 0) + { + m_endPoint = m_udp->Allocate (port); + } + else if (ipv4 != Ipv4Address::GetAny () && port == 0) + { + m_endPoint = m_udp->Allocate (ipv4); + } + else if (ipv4 != Ipv4Address::GetAny () && port != 0) + { + m_endPoint = m_udp->Allocate (ipv4, port); + } + return FinishBind (); } -enum Socket::SocketErrno -UdpSocket::GetErrno (void) const -{ - return m_errno; -} int UdpSocket::ShutdownSend (void) { @@ -124,63 +137,43 @@ UdpSocket::ShutdownRecv (void) return 0; } -void -UdpSocket::DoClose(ns3::Callback > closeCompleted) -{ - // XXX: we should set the close state and check it in all API methods. - if (!closeCompleted.IsNull ()) - { - closeCompleted (this); - } -} -void -UdpSocket::DoConnect(const Ipv4Address & address, - uint16_t portNumber, - ns3::Callback > connectionSucceeded, - ns3::Callback > connectionFailed, - ns3::Callback > halfClose) -{ - m_defaultAddress = address; - m_defaultPort = portNumber; - if (!connectionSucceeded.IsNull ()) - { - connectionSucceeded (this); - } - m_connected = true; -} int -UdpSocket::DoAccept(ns3::Callback, const Ipv4Address&, uint16_t> connectionRequest, - ns3::Callback, const Ipv4Address&, uint16_t> newConnectionCreated, - ns3::Callback > closeRequested) +UdpSocket::Close(void) { - // calling accept on a udp socket is a programming error. - m_errno = ERROR_OPNOTSUPP; - return -1; + NotifyCloseCompleted (); + return 0; +} + +int +UdpSocket::Connect(const Address & address) +{ + InetSocketAddress transport = InetSocketAddress::ConvertFrom (address); + m_defaultAddress = transport.GetIpv4 (); + m_defaultPort = transport.GetPort (); + NotifyConnectionSucceeded (); + m_connected = true; + return 0; } int -UdpSocket::DoSend (const uint8_t* buffer, - uint32_t size, - ns3::Callback, uint32_t> dataSent) +UdpSocket::Send (const Packet &p) { if (!m_connected) { m_errno = ERROR_NOTCONN; return -1; } - Packet p; - if (buffer == 0) - { - p = Packet (size); - } - else - { - p = Packet (buffer, size); - } - return DoSendPacketTo (p, m_defaultAddress, m_defaultPort, dataSent); + return DoSendTo (p, m_defaultAddress, m_defaultPort); } int -UdpSocket::DoSendPacketTo (const Packet &p, Ipv4Address daddr, uint16_t dport, - ns3::Callback, uint32_t> dataSent) +UdpSocket::DoSendTo (const Packet &p, const Address &address) +{ + InetSocketAddress transport = InetSocketAddress::ConvertFrom (address); + Ipv4Address ipv4 = transport.GetIpv4 (); + uint16_t port = transport.GetPort (); + return DoSendTo (p, ipv4, port); +} +int +UdpSocket::DoSendTo (const Packet &p, Ipv4Address ipv4, uint16_t port) { if (m_endPoint == 0) { @@ -196,64 +189,36 @@ UdpSocket::DoSendPacketTo (const Packet &p, Ipv4Address daddr, uint16_t dport, m_errno = ERROR_SHUTDOWN; return -1; } - m_udp->Send (p, m_endPoint->GetLocalAddress (), daddr, - m_endPoint->GetLocalPort (), dport); - if (!dataSent.IsNull ()) - { - dataSent (this, p.GetSize ()); - } + m_udp->Send (p, m_endPoint->GetLocalAddress (), ipv4, + m_endPoint->GetLocalPort (), port); + NotifyDataSent (p.GetSize ()); return 0; } int -UdpSocket::DoSendTo(const Ipv4Address &address, - uint16_t port, - const uint8_t *buffer, - uint32_t size, - ns3::Callback, uint32_t> dataSent) +UdpSocket::SendTo(const Address &address, const Packet &p) { if (m_connected) { m_errno = ERROR_ISCONN; return -1; } - Packet p; - if (buffer == 0) - { - p = Packet (size); - } - else - { - p = Packet (buffer, size); - } - return DoSendPacketTo (p, address, port, dataSent); -} -void -UdpSocket::DoRecv(ns3::Callback, const uint8_t*, uint32_t,const Ipv4Address&, uint16_t> callback) -{ - m_rxCallback = callback; -} -void -UdpSocket::DoRecvDummy(ns3::Callback, uint32_t,const Ipv4Address&, uint16_t> callback) -{ - m_dummyRxCallback = callback; + InetSocketAddress transport = InetSocketAddress::ConvertFrom (address); + Ipv4Address ipv4 = transport.GetIpv4 (); + uint16_t port = transport.GetPort (); + return DoSendTo (p, ipv4, port); } void -UdpSocket::ForwardUp (const Packet &packet, Ipv4Address saddr, uint16_t sport) +UdpSocket::ForwardUp (const Packet &packet, Ipv4Address ipv4, uint16_t port) { if (m_shutdownRecv) { return; } + + Address address = InetSocketAddress (ipv4, port); Packet p = packet; - if (!m_dummyRxCallback.IsNull ()) - { - m_dummyRxCallback (this, p.GetSize (), saddr, sport); - } - if (!m_rxCallback.IsNull ()) - { - m_rxCallback (this, p.PeekData (), p.GetSize (), saddr, sport); - } + NotifyDataReceived (p, address); } }//namespace ns3 diff --git a/src/internet-node/udp-socket.h b/src/internet-node/udp-socket.h index c2923c80f..b3674f7ef 100644 --- a/src/internet-node/udp-socket.h +++ b/src/internet-node/udp-socket.h @@ -25,6 +25,7 @@ #include "ns3/callback.h" #include "ns3/socket.h" #include "ns3/ptr.h" +#include "ns3/ipv4-address.h" namespace ns3 { @@ -45,49 +46,32 @@ public: virtual enum SocketErrno GetErrno (void) const; virtual Ptr GetNode (void) const; virtual int Bind (void); - virtual int Bind (Ipv4Address address); - virtual int Bind (uint16_t port); - virtual int Bind (Ipv4Address address, uint16_t port); + virtual int Bind (const Address &address); + virtual int Close (void); virtual int ShutdownSend (void); virtual int ShutdownRecv (void); + virtual int Connect(const Address &address); + virtual int Send (const Packet &p); + virtual int SendTo(const Address &address,const Packet &p); private: - virtual void DoClose(ns3::Callback > closeCompleted); - virtual void DoConnect(const Ipv4Address & address, - uint16_t portNumber, - ns3::Callback > connectionSucceeded, - ns3::Callback > connectionFailed, - ns3::Callback > halfClose); - virtual int DoAccept(ns3::Callback, const Ipv4Address&, uint16_t> connectionRequest, - ns3::Callback, const Ipv4Address&, uint16_t> newConnectionCreated, - ns3::Callback > closeRequested); - virtual int DoSend (const uint8_t* buffer, - uint32_t size, - ns3::Callback, uint32_t> dataSent); - virtual int DoSendTo(const Ipv4Address &address, - uint16_t port, - const uint8_t *buffer, - uint32_t size, - ns3::Callback, uint32_t> dataSent); - virtual void DoRecv(ns3::Callback, const uint8_t*, uint32_t,const Ipv4Address&, uint16_t>); - virtual void DoRecvDummy(ns3::Callback, uint32_t,const Ipv4Address&, uint16_t>); private: friend class Udp; // invoked by Udp class int FinishBind (void); - void ForwardUp (const Packet &p, Ipv4Address saddr, uint16_t sport); + void ForwardUp (const Packet &p, Ipv4Address ipv4, uint16_t port); void Destroy (void); - int DoSendPacketTo (const Packet &p, Ipv4Address daddr, uint16_t dport, - ns3::Callback, uint32_t> dataSent); + int DoSendTo (const Packet &p, const Address &daddr); + int DoSendTo (const Packet &p, Ipv4Address daddr, uint16_t dport); Ipv4EndPoint *m_endPoint; Ptr m_node; Ptr m_udp; Ipv4Address m_defaultAddress; uint16_t m_defaultPort; - Callback,uint32_t,const Ipv4Address &,uint16_t> m_dummyRxCallback; - Callback,uint8_t const*,uint32_t,const Ipv4Address &,uint16_t> m_rxCallback; + Callback,uint32_t,const Address &> m_dummyRxCallback; + Callback,uint8_t const*,uint32_t,const Address &> m_rxCallback; enum SocketErrno m_errno; bool m_shutdownSend; bool m_shutdownRecv; diff --git a/src/internet-node/wscript b/src/internet-node/wscript index 7a1b1c10e..a5f5ec6ef 100644 --- a/src/internet-node/wscript +++ b/src/internet-node/wscript @@ -2,14 +2,9 @@ def build(bld): - obj = bld.create_obj('cpp', 'shlib') - obj.name = 'ns3-internet-node' - obj.target = obj.name - obj.uselib_local = ['ns3-node', 'ns3-applications'] + obj = bld.create_ns3_module('internet-node', ['node', 'applications']) obj.source = [ 'internet-node.cc', - 'l3-demux.cc', - 'l3-protocol.cc', 'ipv4-l4-demux.cc', 'ipv4-l4-protocol.cc', 'ipv4-header.cc', @@ -27,9 +22,7 @@ def build(bld): 'ipv4-loopback-interface.cc', 'udp-socket.cc', 'ipv4-end-point-demux.cc', - 'arp-private.cc', 'ipv4-impl.cc', - 'ipv4-private.cc', 'ascii-trace.cc', 'pcap-trace.cc', 'udp-impl.cc', @@ -41,4 +34,5 @@ def build(bld): 'ascii-trace.h', 'pcap-trace.h', 'ipv4-header.h', + 'udp-header.h', ] diff --git a/src/mobility/mobility-model.cc b/src/mobility/mobility-model.cc index 959e29a1e..808c5f4dd 100644 --- a/src/mobility/mobility-model.cc +++ b/src/mobility/mobility-model.cc @@ -52,9 +52,9 @@ MobilityModel::Set (const Position &position) } double -MobilityModel::GetDistanceFrom (const MobilityModel &other) const +MobilityModel::GetDistanceFrom (Ptr other) const { - Position oPosition = other.DoGet (); + Position oPosition = other->DoGet (); Position position = DoGet (); return CalculateDistance (position, oPosition); } diff --git a/src/mobility/mobility-model.h b/src/mobility/mobility-model.h index 4c4cf9000..5d0e6ee4a 100644 --- a/src/mobility/mobility-model.h +++ b/src/mobility/mobility-model.h @@ -57,7 +57,7 @@ public: * \param position a reference to another mobility model * \returns the distance between the two objects. Unit is meters. */ - double GetDistanceFrom (const MobilityModel &position) const; + double GetDistanceFrom (Ptr position) const; protected: /** * Must be invoked by subclasses when the course of the diff --git a/src/mobility/position.h b/src/mobility/position.h index 884ba50b0..d34fadfb2 100644 --- a/src/mobility/position.h +++ b/src/mobility/position.h @@ -1,3 +1,4 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ #ifndef POSITION_H #define POSITION_H diff --git a/src/mobility/random-position.h b/src/mobility/random-position.h index ee97a2286..8fcc54a6d 100644 --- a/src/mobility/random-position.h +++ b/src/mobility/random-position.h @@ -1,3 +1,4 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ #ifndef RANDOM_POSITION_H #define RANDOM_POSITION_H diff --git a/src/mobility/speed.h b/src/mobility/speed.h index 5e2ad3c45..317fc4333 100644 --- a/src/mobility/speed.h +++ b/src/mobility/speed.h @@ -1,3 +1,4 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ #ifndef SPEED_H #define SPEED_H diff --git a/src/mobility/static-speed-helper.h b/src/mobility/static-speed-helper.h index 0ba541b75..8737f2edd 100644 --- a/src/mobility/static-speed-helper.h +++ b/src/mobility/static-speed-helper.h @@ -1,3 +1,4 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ #ifndef STATIC_SPEED_HELPER_H #define STATIC_SPEED_HELPER_H diff --git a/src/mobility/wscript b/src/mobility/wscript index b20c5af67..66d9dfb4f 100644 --- a/src/mobility/wscript +++ b/src/mobility/wscript @@ -1,10 +1,7 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- def build(bld): - mobility = bld.create_obj('cpp', 'shlib') - mobility.name = 'ns3-mobility' - mobility.target = mobility.name - mobility.uselib_local = ['ns3-core', 'ns3-simulator'] + mobility = bld.create_ns3_module('mobility', ['core', 'simulator']) mobility.source = [ 'grid-topology.cc', 'hierarchical-mobility-model.cc', diff --git a/src/node/address-utils.cc b/src/node/address-utils.cc index 702db4dd5..08c5b1736 100644 --- a/src/node/address-utils.cc +++ b/src/node/address-utils.cc @@ -26,24 +26,34 @@ void WriteTo (Buffer::Iterator &i, Ipv4Address ad) { i.WriteHtonU32 (ad.GetHostOrder ()); } -void WriteTo (Buffer::Iterator &i, MacAddress ad) +void WriteTo (Buffer::Iterator &i, const Address &ad) { - uint8_t mac[MacAddress::MAX_LEN]; - ad.Peek (mac); + uint8_t mac[Address::MAX_SIZE]; + ad.CopyTo (mac); i.Write (mac, ad.GetLength ()); } +void WriteTo (Buffer::Iterator &i, Eui48Address ad) +{ + uint8_t mac[6]; + ad.CopyTo (mac); + i.Write (mac, 6); +} void ReadFrom (Buffer::Iterator &i, Ipv4Address &ad) { ad.SetHostOrder (i.ReadNtohU32 ()); } -void ReadFrom (Buffer::Iterator &i, MacAddress &ad, uint32_t len) +void ReadFrom (Buffer::Iterator &i, Address &ad, uint32_t len) { - uint8_t mac[MacAddress::MAX_LEN]; + uint8_t mac[Address::MAX_SIZE]; i.Read (mac, len); - ad.Set (mac, len); + ad.CopyFrom (mac, len); +} +void ReadFrom (Buffer::Iterator &i, Eui48Address &ad) +{ + uint8_t mac[6]; + i.Read (mac, 6); + ad.CopyFrom (mac); } - - -}; // namespace ns3 +} // namespace ns3 diff --git a/src/node/address-utils.h b/src/node/address-utils.h index 9dbe8fc55..1403c544a 100644 --- a/src/node/address-utils.h +++ b/src/node/address-utils.h @@ -22,16 +22,19 @@ #define ADDRESS_UTILS_H #include "ns3/buffer.h" -#include "ns3/ipv4-address.h" -#include "ns3/mac-address.h" +#include "ipv4-address.h" +#include "address.h" +#include "eui48-address.h" namespace ns3 { void WriteTo (Buffer::Iterator &i, Ipv4Address ad); -void WriteTo (Buffer::Iterator &i, MacAddress ad); +void WriteTo (Buffer::Iterator &i, const Address &ad); +void WriteTo (Buffer::Iterator &i, Eui48Address ad); void ReadFrom (Buffer::Iterator &i, Ipv4Address &ad); -void ReadFrom (Buffer::Iterator &i, MacAddress &ad, uint32_t len); +void ReadFrom (Buffer::Iterator &i, Address &ad, uint32_t len); +void ReadFrom (Buffer::Iterator &i, Eui48Address &ad); }; diff --git a/src/node/address.cc b/src/node/address.cc new file mode 100644 index 000000000..0cc37b1a2 --- /dev/null +++ b/src/node/address.cc @@ -0,0 +1,164 @@ +#include "ns3/assert.h" +#include "address.h" +#include +#include + +namespace ns3 { + +Address::Address () + : m_type (0), + m_len (0) +{ + memset (m_data, 0, m_len); +} + +Address::Address (uint8_t type, const uint8_t *buffer, uint8_t len) + : m_type (type), + m_len (len) +{ + NS_ASSERT (m_len <= MAX_SIZE); + memset (m_data, 0, m_len); + memcpy (m_data, buffer, m_len); +} +Address::Address (const Address & address) + : m_type (address.m_type), + m_len (address.m_len) +{ + NS_ASSERT (m_len <= MAX_SIZE); + memset (m_data, 0, m_len); + memcpy (m_data, address.m_data, m_len); +} +Address & +Address::operator = (const Address &address) +{ + NS_ASSERT (m_len <= MAX_SIZE); + m_type = address.m_type; + m_len = address.m_len; + NS_ASSERT (m_len <= MAX_SIZE); + memset (m_data, 0, m_len); + memcpy (m_data, address.m_data, m_len); + return *this; +} + +bool +Address::IsInvalid (void) const +{ + return m_len == 0 && m_type == 0; +} + +uint8_t +Address::GetLength (void) const +{ + NS_ASSERT (m_len <= MAX_SIZE); + return m_len; +} +uint32_t +Address::CopyTo (uint8_t buffer[MAX_SIZE]) const +{ + NS_ASSERT (m_len <= MAX_SIZE); + memcpy (buffer, m_data, m_len); + return m_len; +} +uint32_t +Address::CopyAllTo (uint8_t *buffer, uint8_t len) const +{ + NS_ASSERT (len >= m_len + 2); + buffer[0] = m_type; + buffer[1] = m_len; + memcpy (buffer + 2, m_data, m_len); + return m_len + 2; +} + +uint32_t +Address::CopyFrom (const uint8_t *buffer, uint8_t len) +{ + NS_ASSERT (len <= MAX_SIZE); + memcpy (m_data, buffer, len); + m_len = len; + return m_len; +} +uint32_t +Address::CopyAllFrom (const uint8_t *buffer, uint8_t len) +{ + NS_ASSERT (len >= 2); + m_type = buffer[0]; + m_len = buffer[1]; + NS_ASSERT (len >= m_len + 2); + memcpy (m_data, buffer + 2, m_len); + return m_len + 2; +} +bool +Address::CheckCompatible (uint8_t type, uint8_t len) const +{ + NS_ASSERT (len <= MAX_SIZE); + return m_len == len && (m_type == type || m_type == 0); +} +bool +Address::IsMatchingType (uint8_t type) const +{ + return m_type == type; +} + +uint8_t +Address::Register (void) +{ + static uint8_t type = 1; + type++; + return type; +} + +bool operator == (const Address &a, const Address &b) +{ + NS_ASSERT (a.m_type == b.m_type || + a.m_type == 0 || + b.m_type == 0); + NS_ASSERT (a.GetLength() == b.GetLength()); + return memcmp (a.m_data, b.m_data, a.m_len) == 0; +} +bool operator != (const Address &a, const Address &b) +{ + return !(a == b); +} +bool operator < (const Address &a, const Address &b) +{ + NS_ASSERT (a.m_type == b.m_type || + a.m_type == 0 || + b.m_type == 0); + NS_ASSERT (a.GetLength() == b.GetLength()); + for (uint8_t i = 0; i < a.GetLength(); i++) + { + if (a.m_data[i] < b.m_data[i]) + { + return true; + } + else if (a.m_data[i] > b.m_data[i]) + { + return false; + } + } + return false; +} + +std::ostream& operator<< (std::ostream& os, const Address & address) +{ + if (address.m_len == 0) + { + os << "NULL-ADDRESS"; + return os; + } + os.setf (std::ios::hex, std::ios::basefield); + os.fill('0'); + for (uint8_t i=0; i < (address.m_len-1); i++) + { + os << std::setw(2) << (uint32_t)address.m_data[i] << ":"; + } + // Final byte not suffixed by ":" + os << std::setw(2) << (uint32_t)address.m_data[address.m_len-1]; + os.setf (std::ios::dec, std::ios::basefield); + os.fill(' '); + return os; +} + + + +} // namespace ns3 diff --git a/src/node/address.h b/src/node/address.h new file mode 100644 index 000000000..aedc940d4 --- /dev/null +++ b/src/node/address.h @@ -0,0 +1,172 @@ +#ifndef ADDRESS_H +#define ADDRESS_H + +#include +#include + +namespace ns3 { + +/** + * \brief a polymophic address class + * + * This class is very similar in design and spirit to the BSD sockaddr + * structure: they are both used to hold multiple types of addresses + * together with the type of the address. + * + * A new address class defined by a user needs to: + * - allocate a type id with Address::Register + * - provide a method to convert his new address to an Address + * instance. This method is typically a member method named ConvertTo: + * Address MyAddress::ConvertTo (void) const; + * - provide a method to convert an Address instance back to + * an instance of his new address type. This method is typically + * a static member method of his address class named ConvertFrom: + * static MyAddress MyAddress::ConvertFrom (const Address &address); + * - the ConvertFrom method is expected to check that the type of the + * input Address instance is compatible with its own type. + * + * Typical code to create a new class type looks like: + * \code + * // this class represents addresses which are 2 bytes long. + * class MyAddress + * { + * public: + * Address ConvertTo (void) const; + * static MyAddress ConvertFrom (void); + * private: + * static uint8_t GetType (void); + * }; + * + * Address MyAddress::ConvertTo (void) const + * { + * return Address (GetType (), m_buffer, 2); + * } + * MyAddress MyAddress::ConvertFrom (const Address &address) + * { + * MyAddress ad; + * NS_ASSERT (address.CheckCompatible (GetType (), 2)); + * address.CopyTo (ad.m_buffer, 2); + * return ad; + * } + * uint8_t MyAddress::GetType (void) + * { + * static uint8_t type = Address::Register (); + * return type; + * } + * \endcode + */ +class Address +{ +public: + enum { + /** + * The maximum size of a byte buffer which + * can be stored in an Address instance. + */ + MAX_SIZE = 30 + }; + + /** + * Create an invalid address + */ + Address (); + /** + * \param type the type of the Address to create + * \param buffer a pointer to a buffer of bytes which hold + * a serialized representation of the address in network + * byte order. + * \param len the length of the buffer. + * + * Create an address from a type and a buffer. This constructor + * is typically invoked from the conversion functions of various + * address types when they have to convert themselves to an + * Address instance. + */ + Address (uint8_t type, const uint8_t *buffer, uint8_t len); + Address (const Address & address); + Address &operator = (const Address &address); + + /** + * \returns true if this address is invalid, false otherwise. + * + * An address is invalid if and only if it was created + * through the default constructor and it was never + * re-initialized. + */ + bool IsInvalid (void) const; + /** + * \returns the length of the underlying address. + */ + uint8_t GetLength (void) const; + /** + * \param buffer buffer to copy the address bytes to. + * \returns the number of bytes copied. + */ + uint32_t CopyTo (uint8_t buffer[MAX_SIZE]) const; + /** + * \param buffer buffer to copy the whole address data structure to + * \param len the size of the buffer + * \returns the number of bytes copied. + */ + uint32_t CopyAllTo (uint8_t *buffer, uint8_t len) const; + /** + * \param buffer pointer to a buffer of bytes which contain + * a serialized representation of the address in network + * byte order. + * \param len length of buffer + * \returns the number of bytes copied. + * + * Copy the input buffer to the internal buffer of this address + * instance. + */ + uint32_t CopyFrom (const uint8_t *buffer, uint8_t len); + /** + * \param buffer pointer to a buffer of bytes which contain + * a copy of all the members of this Address class. + * \param len the length of the buffer + * \returns the number of bytes copied. + */ + uint32_t CopyAllFrom (const uint8_t *buffer, uint8_t len); + /** + * \param type a type id as returned by Address::Register + * \param len the length associated to this type id. + * + * \returns true if the type of the address stored internally + * is compatible with the requested type, false otherwise. + */ + bool CheckCompatible (uint8_t type, uint8_t len) const; + /** + * \param type a type id as returned by Address::Register + * \returns true if the type of the address stored internally + * is compatible with the requested type, false otherwise. + * + * This method checks that the types are _exactly_ equal. + * This method is really used only by the PacketSocketAddress + * and there is little point in using it otherwise so, + * you have been warned: DO NOT USE THIS METHOD. + */ + bool IsMatchingType (uint8_t type) const; + /** + * Allocate a new type id for a new type of address. + * \returns a new type id. + */ + static uint8_t Register (void); +private: + friend bool operator == (const Address &a, const Address &b); + friend bool operator < (const Address &a, const Address &b); + friend std::ostream& operator<< (std::ostream& os, const Address & address); + + uint8_t m_type; + uint8_t m_len; + uint8_t m_data[MAX_SIZE]; +}; + +bool operator == (const Address &a, const Address &b); +bool operator != (const Address &a, const Address &b); +bool operator < (const Address &a, const Address &b); +std::ostream& operator<< (std::ostream& os, const Address & address); + +} // namespace ns3 + + +#endif /* ADDRESS_H */ diff --git a/src/node/channel.h b/src/node/channel.h index b2feb280b..5e72e1ae5 100644 --- a/src/node/channel.h +++ b/src/node/channel.h @@ -1,7 +1,5 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2007 University of Washington - * * 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; @@ -14,11 +12,8 @@ * 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: Craig Dowell - * - * Wed Feb 14 16:05:46 PST 2007 craigdo: Created */ + #ifndef CHANNEL_H #define CHANNEL_H @@ -41,6 +36,7 @@ class Channel : public Object { public: static const InterfaceId iid; + Channel (); Channel (std::string name); @@ -62,8 +58,8 @@ public: virtual Ptr GetDevice (uint32_t i) const = 0; protected: - virtual ~Channel (); - std::string m_name; + virtual ~Channel (); + std::string m_name; private: }; diff --git a/src/node/ethernet-header.cc b/src/node/ethernet-header.cc index 73172d3e5..88803b4f5 100644 --- a/src/node/ethernet-header.cc +++ b/src/node/ethernet-header.cc @@ -19,6 +19,8 @@ * Author: Emmanuelle Laprise */ +#include +#include #include "ns3/assert.h" #include "ns3/debug.h" #include "ns3/header.h" @@ -29,6 +31,15 @@ NS_DEBUG_COMPONENT_DEFINE ("EthernetHeader"); namespace ns3 { +NS_HEADER_ENSURE_REGISTERED (EthernetHeader); + +uint32_t +EthernetHeader::GetUid (void) +{ + static uint32_t uid = AllocateUid ("EthernetHeader.ns3"); + return uid; +} + EthernetHeader::EthernetHeader (bool hasPreamble) : m_enPreambleSfd (hasPreamble), m_lengthType (0) @@ -39,9 +50,6 @@ EthernetHeader::EthernetHeader () m_lengthType (0) {} -EthernetHeader::~EthernetHeader () -{} - void EthernetHeader::SetLengthType (uint16_t lengthType) { @@ -65,22 +73,22 @@ EthernetHeader::GetPreambleSfd (void) const } void -EthernetHeader::SetSource (MacAddress source) +EthernetHeader::SetSource (Eui48Address source) { m_source = source; } -MacAddress +Eui48Address EthernetHeader::GetSource (void) const { return m_source; } void -EthernetHeader::SetDestination (MacAddress dst) +EthernetHeader::SetDestination (Eui48Address dst) { m_destination = dst; } -MacAddress +Eui48Address EthernetHeader::GetDestination (void) const { return m_destination; @@ -99,21 +107,21 @@ EthernetHeader::GetHeaderSize (void) const } std::string -EthernetHeader::DoGetName (void) const +EthernetHeader::GetName (void) const { return "ETHERNET"; } void -EthernetHeader::PrintTo (std::ostream &os) const +EthernetHeader::Print (std::ostream &os) const { // ethernet, right ? - os << "(ethernet)"; if (m_enPreambleSfd) { os << " preamble/sfd=" << m_preambleSfd << ","; } - os << " length/type=" << m_lengthType + + os << " length/type=0x" << std::hex << m_lengthType << std::dec << ", source=" << m_source << ", destination=" << m_destination; } @@ -123,13 +131,15 @@ EthernetHeader::GetSerializedSize (void) const if (m_enPreambleSfd) { return PREAMBLE_SIZE + LENGTH_SIZE + 2*MAC_ADDR_SIZE; - } else { + } + else + { return LENGTH_SIZE + 2*MAC_ADDR_SIZE; } } void -EthernetHeader::SerializeTo (Buffer::Iterator start) const +EthernetHeader::Serialize (Buffer::Iterator start) const { Buffer::Iterator i = start; @@ -137,14 +147,12 @@ EthernetHeader::SerializeTo (Buffer::Iterator start) const { i.WriteU64(m_preambleSfd); } - NS_ASSERT (m_destination.GetLength () == MAC_ADDR_SIZE); - NS_ASSERT (m_source.GetLength () == MAC_ADDR_SIZE); WriteTo (i, m_destination); WriteTo (i, m_source); i.WriteU16 (m_lengthType); } uint32_t -EthernetHeader::DeserializeFrom (Buffer::Iterator start) +EthernetHeader::Deserialize (Buffer::Iterator start) { Buffer::Iterator i = start; @@ -153,11 +161,11 @@ EthernetHeader::DeserializeFrom (Buffer::Iterator start) m_enPreambleSfd = i.ReadU64 (); } - ReadFrom (i, m_destination, MAC_ADDR_SIZE); - ReadFrom (i, m_source, MAC_ADDR_SIZE); + ReadFrom (i, m_destination); + ReadFrom (i, m_source); m_lengthType = i.ReadU16 (); return GetSerializedSize (); } -}; // namespace ns3 +} // namespace ns3 diff --git a/src/node/ethernet-header.h b/src/node/ethernet-header.h index f16db2976..b1b3d1998 100644 --- a/src/node/ethernet-header.h +++ b/src/node/ethernet-header.h @@ -23,7 +23,8 @@ #define ETHERNET_HEADER_H #include "ns3/header.h" -#include "ns3/mac-address.h" +#include +#include "ns3/eui48-address.h" namespace ns3 { @@ -45,11 +46,10 @@ namespace ns3 { * the packet. Eventually the class will be improved to also support * VLAN tags in packet headers. */ -class EthernetHeader : public Header { +class EthernetHeader : public Header +{ public: - static const int PREAMBLE_SIZE = 8; /// size of the preamble_sfd header field - static const int LENGTH_SIZE = 2; /// size of the length_type header field - static const int MAC_ADDR_SIZE = 6; /// size of src/dest addr header fields + static uint32_t GetUid (void); /** * \brief Construct a null ethernet header @@ -62,7 +62,6 @@ public: * By default, does not add or remove an ethernet preamble */ EthernetHeader (); - virtual ~EthernetHeader (); /** * \param size The size of the payload in bytes */ @@ -70,11 +69,11 @@ public: /** * \param source The source address of this packet */ - void SetSource (MacAddress source); + void SetSource (Eui48Address source); /** * \param destination The destination address of this packet. */ - void SetDestination (MacAddress destination); + void SetDestination (Eui48Address destination); /** * \param preambleSfd The value that the preambleSfd field should take */ @@ -90,11 +89,11 @@ public: /** * \return The source address of this packet */ - MacAddress GetSource (void) const; + Eui48Address GetSource (void) const; /** * \return The destination address of this packet */ - MacAddress GetDestination (void) const; + Eui48Address GetDestination (void) const; /** * \return The value of the PreambleSfd field */ @@ -104,12 +103,15 @@ public: */ uint32_t GetHeaderSize() const; + std::string GetName (void) const; + void Print (std::ostream &os) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator start) const; + uint32_t Deserialize (Buffer::Iterator start); private: - virtual std::string DoGetName (void) const; - virtual void PrintTo (std::ostream &os) const; - virtual uint32_t GetSerializedSize (void) const; - virtual void SerializeTo (Buffer::Iterator start) const; - virtual uint32_t DeserializeFrom (Buffer::Iterator start); + static const int PREAMBLE_SIZE = 8; /// size of the preamble_sfd header field + static const int LENGTH_SIZE = 2; /// size of the length_type header field + static const int MAC_ADDR_SIZE = 6; /// size of src/dest addr header fields /** * If false, the preamble/sfd are not serialised/deserialised. @@ -117,8 +119,8 @@ private: bool m_enPreambleSfd; uint64_t m_preambleSfd; /// Value of the Preamble/SFD fields uint16_t m_lengthType; /// Length or type of the packet - MacAddress m_source; /// Source address - MacAddress m_destination; /// Destination address + Eui48Address m_source; /// Source address + Eui48Address m_destination; /// Destination address }; }; // namespace ns3 diff --git a/src/node/ethernet-trailer.cc b/src/node/ethernet-trailer.cc index 6aa622b66..6fdb5a845 100644 --- a/src/node/ethernet-trailer.cc +++ b/src/node/ethernet-trailer.cc @@ -28,16 +28,22 @@ NS_DEBUG_COMPONENT_DEFINE ("EthernetTrailer"); namespace ns3 { +NS_TRAILER_ENSURE_REGISTERED (EthernetTrailer); + bool EthernetTrailer::m_calcFcs = false; +uint32_t +EthernetTrailer::GetUid (void) +{ + static uint32_t uid = AllocateUid ("EthernetTrailer.ns3"); + return uid; +} + EthernetTrailer::EthernetTrailer () { Init(); } -EthernetTrailer::~EthernetTrailer () -{} - void EthernetTrailer::Init() { m_fcs = 0; @@ -85,13 +91,13 @@ EthernetTrailer::GetTrailerSize (void) const return GetSerializedSize(); } std::string -EthernetTrailer::DoGetName (void) const +EthernetTrailer::GetName (void) const { return "ETHERNET"; } void -EthernetTrailer::PrintTo (std::ostream &os) const +EthernetTrailer::Print (std::ostream &os) const { os << " fcs=" << m_fcs; } @@ -102,7 +108,7 @@ EthernetTrailer::GetSerializedSize (void) const } void -EthernetTrailer::SerializeTo (Buffer::Iterator end) const +EthernetTrailer::Serialize (Buffer::Iterator end) const { Buffer::Iterator i = end; i.Prev(GetSerializedSize()); @@ -110,7 +116,7 @@ EthernetTrailer::SerializeTo (Buffer::Iterator end) const i.WriteU32 (m_fcs); } uint32_t -EthernetTrailer::DeserializeFrom (Buffer::Iterator end) +EthernetTrailer::Deserialize (Buffer::Iterator end) { Buffer::Iterator i = end; uint32_t size = GetSerializedSize(); diff --git a/src/node/ethernet-trailer.h b/src/node/ethernet-trailer.h index 7ec3e162d..c6deeb6ab 100644 --- a/src/node/ethernet-trailer.h +++ b/src/node/ethernet-trailer.h @@ -24,6 +24,7 @@ #include "ns3/trailer.h" #include "ns3/packet.h" +#include namespace ns3 { /** @@ -33,13 +34,16 @@ namespace ns3 { * ethernet packet. The actual FCS functionality is not yet coded and * so this acts more as a placeholder. */ -class EthernetTrailer : public Trailer { +class EthernetTrailer : public Trailer +{ public: + static uint32_t GetUid (void); + /** * \brief Construct a null ethernet trailer */ EthernetTrailer (); - virtual ~EthernetTrailer (); + /** * \brief Enable or disabled FCS checking and calculations * \param enable If true, enables FCS calculations. @@ -77,12 +81,12 @@ public: */ uint32_t GetTrailerSize() const; + std::string GetName (void) const; + void Print (std::ostream &os) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator end) const; + uint32_t Deserialize (Buffer::Iterator end); private: - virtual std::string DoGetName (void) const; - virtual void PrintTo (std::ostream &os) const; - virtual uint32_t GetSerializedSize (void) const; - virtual void SerializeTo (Buffer::Iterator end) const; - virtual uint32_t DeserializeFrom (Buffer::Iterator end); /** * Initializes the trailer parameters during construction. @@ -98,7 +102,7 @@ private: }; -}; // namespace ns3 +} // namespace ns3 #endif /* ETHERNET_TRAILER_H */ diff --git a/src/node/eui48-address.cc b/src/node/eui48-address.cc new file mode 100644 index 000000000..d197a0e52 --- /dev/null +++ b/src/node/eui48-address.cc @@ -0,0 +1,168 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 INRIA + * + * 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: Mathieu Lacage + */ +#include "eui48-address.h" +#include "address.h" +#include "ns3/assert.h" +#include +#include + +namespace ns3 { + +#define ASCII_a (0x41) +#define ASCII_z (0x5a) +#define ASCII_A (0x61) +#define ASCII_Z (0x7a) +#define ASCII_COLON (0x3a) +#define ASCII_ZERO (0x30) + +static char +AsciiToLowCase (char c) +{ + if (c >= ASCII_a && c <= ASCII_z) { + return c; + } else if (c >= ASCII_A && c <= ASCII_Z) { + return c + (ASCII_a - ASCII_A); + } else { + return c; + } +} + + +Eui48Address::Eui48Address () +{ + memset (m_address, 0, 6); +} +Eui48Address::Eui48Address (const char *str) +{ + int i = 0; + while (*str != 0 && i < 6) + { + uint8_t byte = 0; + while (*str != ASCII_COLON && *str != 0) + { + byte <<= 4; + char low = AsciiToLowCase (*str); + if (low >= ASCII_a) + { + byte |= low - ASCII_a + 10; + } + else + { + byte |= low - ASCII_ZERO; + } + str++; + } + m_address[i] = byte; + i++; + if (*str == 0) + { + break; + } + str++; + } + NS_ASSERT (i == 6); +} +void +Eui48Address::CopyFrom (const uint8_t buffer[6]) +{ + memcpy (m_address, buffer, 6); +} +void +Eui48Address::CopyTo (uint8_t buffer[6]) const +{ + memcpy (buffer, m_address, 6); +} + +bool +Eui48Address::IsMatchingType (const Address &address) +{ + return address.CheckCompatible (GetType (), 6); +} +Eui48Address::operator Address () +{ + return ConvertTo (); +} +Address +Eui48Address::ConvertTo (void) const +{ + return Address (GetType (), m_address, 6); +} +Eui48Address +Eui48Address::ConvertFrom (const Address &address) +{ + NS_ASSERT (address.CheckCompatible (GetType (), 6)); + Eui48Address retval; + address.CopyTo (retval.m_address); + return retval; +} +Eui48Address +Eui48Address::Allocate (void) +{ + static uint64_t id = 0; + id++; + Eui48Address address; + address.m_address[0] = (id >> 40) & 0xff; + address.m_address[1] = (id >> 32) & 0xff; + address.m_address[2] = (id >> 24) & 0xff; + address.m_address[3] = (id >> 16) & 0xff; + address.m_address[4] = (id >> 8) & 0xff; + address.m_address[5] = (id >> 0) & 0xff; + return address; +} +uint8_t +Eui48Address::GetType (void) +{ + static uint8_t type = Address::Register (); + return type; +} + +bool operator == (const Eui48Address &a, const Eui48Address &b) +{ + uint8_t ada[6]; + uint8_t adb[6]; + a.CopyTo (ada); + b.CopyTo (adb); + return memcmp (ada, adb, 6) == 0; +} +bool operator != (const Eui48Address &a, const Eui48Address &b) +{ + return ! (a == b); +} + +std::ostream& operator<< (std::ostream& os, const Eui48Address & address) +{ + uint8_t ad[6]; + address.CopyTo (ad); + + os.setf (std::ios::hex, std::ios::basefield); + os.fill('0'); + for (uint8_t i=0; i < 5; i++) + { + os << std::setw(2) << (uint32_t)ad[i] << ":"; + } + // Final byte not suffixed by ":" + os << std::setw(2) << (uint32_t)ad[5]; + os.setf (std::ios::dec, std::ios::basefield); + os.fill(' '); + return os; +} + + +} // namespace ns3 diff --git a/src/node/eui48-address.h b/src/node/eui48-address.h new file mode 100644 index 000000000..bd778a444 --- /dev/null +++ b/src/node/eui48-address.h @@ -0,0 +1,99 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 INRIA + * + * 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: Mathieu Lacage + */ +#ifndef EUI48_ADDRESS_H +#define EUI48_ADDRESS_H + +#include +#include + +namespace ns3 { + +class Address; + +/** + * \brief an EUI-48 address + * + * This class can contain 48 bit IEEE addresses. + */ +class Eui48Address +{ +public: + Eui48Address (); + /** + * \param str a string representing the new Eui48Address + * + * The format of the string is "xx:xx:xx:xx:xx:xx" + */ + Eui48Address (const char *str); + + /** + * \param buffer address in network order + * + * Copy the input address to our internal buffer. + */ + void CopyFrom (const uint8_t buffer[6]); + /** + * \param buffer address in network order + * + * Copy the internal address to the input buffer. + */ + void CopyTo (uint8_t buffer[6]) const; + + /** + * \returns a new Address instance + * + * Convert an instance of this class to a polymorphic Address instance. + */ + operator Address (); + /** + * \param address a polymorphic address + * \returns a new Eui48Address from the polymorphic address + * + * This function performs a type check and asserts if the + * type of the input address is not compatible with an + * Eui48Address. + */ + static Eui48Address ConvertFrom (const Address &address); + /** + * \returns true if the address matches, false otherwise. + */ + static bool IsMatchingType (const Address &address); + /** + * Allocate a new Eui48Address. + */ + static Eui48Address Allocate (void); +private: + /** + * \returns a new Address instance + * + * Convert an instance of this class to a polymorphic Address instance. + */ + Address ConvertTo (void) const; + static uint8_t GetType (void); + uint8_t m_address[6]; +}; + +bool operator == (const Eui48Address &a, const Eui48Address &b); +bool operator != (const Eui48Address &a, const Eui48Address &b); +std::ostream& operator<< (std::ostream& os, const Eui48Address & address); + +} // namespace ns3 + +#endif /* EUI48_ADDRESS_H */ diff --git a/src/node/eui64-address.cc b/src/node/eui64-address.cc new file mode 100644 index 000000000..9cbbba381 --- /dev/null +++ b/src/node/eui64-address.cc @@ -0,0 +1,171 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 INRIA + * + * 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: Mathieu Lacage + */ +#include "eui64-address.h" +#include "address.h" +#include "ns3/assert.h" +#include +#include + +namespace ns3 { + +#define ASCII_a (0x41) +#define ASCII_z (0x5a) +#define ASCII_A (0x61) +#define ASCII_Z (0x7a) +#define ASCII_COLON (0x3a) +#define ASCII_ZERO (0x30) + +static char +AsciiToLowCase (char c) +{ + if (c >= ASCII_a && c <= ASCII_z) { + return c; + } else if (c >= ASCII_A && c <= ASCII_Z) { + return c + (ASCII_a - ASCII_A); + } else { + return c; + } +} + + +Eui64Address::Eui64Address () +{ + memset (m_address, 0, 8); +} +Eui64Address::Eui64Address (const char *str) +{ + int i = 0; + while (*str != 0 && i < 8) + { + uint8_t byte = 0; + while (*str != ASCII_COLON && *str != 0) + { + byte <<= 4; + char low = AsciiToLowCase (*str); + if (low >= ASCII_a) + { + byte |= low - ASCII_a + 10; + } + else + { + byte |= low - ASCII_ZERO; + } + str++; + } + m_address[i] = byte; + i++; + if (*str == 0) + { + break; + } + str++; + } + NS_ASSERT (i == 6); +} +void +Eui64Address::CopyFrom (const uint8_t buffer[8]) +{ + memcpy (m_address, buffer, 8); +} +void +Eui64Address::CopyTo (uint8_t buffer[8]) const +{ + memcpy (buffer, m_address, 8); +} + +bool +Eui64Address::IsMatchingType (const Address &address) +{ + return address.CheckCompatible (GetType (), 8); +} +Eui64Address::operator Address () +{ + return ConvertTo (); +} +Eui64Address +Eui64Address::ConvertFrom (const Address &address) +{ + NS_ASSERT (address.CheckCompatible (GetType (), 8)); + Eui64Address retval; + address.CopyTo (retval.m_address); + return retval; +} +Address +Eui64Address::ConvertTo (void) const +{ + return Address (GetType (), m_address, 8); +} + +Eui64Address +Eui64Address::Allocate (void) +{ + static uint64_t id = 0; + id++; + Eui64Address address; + address.m_address[0] = (id >> 56) & 0xff; + address.m_address[1] = (id >> 48) & 0xff; + address.m_address[2] = (id >> 40) & 0xff; + address.m_address[3] = (id >> 32) & 0xff; + address.m_address[4] = (id >> 24) & 0xff; + address.m_address[5] = (id >> 16) & 0xff; + address.m_address[6] = (id >> 8) & 0xff; + address.m_address[7] = (id >> 0) & 0xff; + return address; +} +uint8_t +Eui64Address::GetType (void) +{ + static uint8_t type = Address::Register (); + return type; +} + +bool operator == (const Eui64Address &a, const Eui64Address &b) +{ + uint8_t ada[8]; + uint8_t adb[8]; + a.CopyTo (ada); + b.CopyTo (adb); + return memcmp (ada, adb, 8) == 0; +} +bool operator != (const Eui64Address &a, const Eui64Address &b) +{ + return ! (a == b); +} + +std::ostream& operator<< (std::ostream& os, const Eui64Address & address) +{ + uint8_t ad[8]; + address.CopyTo (ad); + + os.setf (std::ios::hex, std::ios::basefield); + os.fill('0'); + for (uint8_t i=0; i < 7; i++) + { + os << std::setw(2) << (uint32_t)ad[i] << ":"; + } + // Final byte not suffixed by ":" + os << std::setw(2) << (uint32_t)ad[7]; + os.setf (std::ios::dec, std::ios::basefield); + os.fill(' '); + return os; +} + + +} // namespace ns3 diff --git a/src/node/eui64-address.h b/src/node/eui64-address.h new file mode 100644 index 000000000..98b930057 --- /dev/null +++ b/src/node/eui64-address.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 INRIA + * + * 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: Mathieu Lacage + */ +#ifndef EUI64_ADDRESS_H +#define EUI64_ADDRESS_H + +#include +#include + +namespace ns3 { + +class Address; + +/** + * \brief an EUI-64 address + * + * This class can contain 64 bit IEEE addresses. + */ +class Eui64Address +{ +public: + Eui64Address (); + /** + * \param str a string representing the new Eui64Address + * + * The format of the string is "xx:xx:xx:xx:xx:xx" + */ + Eui64Address (const char *str); + + /** + * \param buffer address in network order + * + * Copy the input address to our internal buffer. + */ + void CopyFrom (const uint8_t buffer[8]); + /** + * \param buffer address in network order + * + * Copy the internal address to the input buffer. + */ + void CopyTo (uint8_t buffer[8]) const; + /** + * \returns a new Address instance + * + * Convert an instance of this class to a polymorphic Address instance. + */ + operator Address (); + /** + * \param address a polymorphic address + * \returns a new Eui64Address from the polymorphic address + * + * This function performs a type check and asserts if the + * type of the input address is not compatible with an + * Eui64Address. + */ + static Eui64Address ConvertFrom (const Address &address); + /** + * \returns true if the address matches, false otherwise. + */ + static bool IsMatchingType (const Address &address); + /** + * Allocate a new Eui64Address. + */ + static Eui64Address Allocate (void); +private: + /** + * \returns a new Address instance + * + * Convert an instance of this class to a polymorphic Address instance. + */ + Address ConvertTo (void) const; + static uint8_t GetType (void); + uint8_t m_address[8]; +}; + +bool operator == (const Eui64Address &a, const Eui64Address &b); +bool operator != (const Eui64Address &a, const Eui64Address &b); +std::ostream& operator<< (std::ostream& os, const Eui64Address & address); + +} // namespace ns3 + +#endif /* EUI64_ADDRESS_H */ diff --git a/src/node/inet-socket-address.cc b/src/node/inet-socket-address.cc new file mode 100644 index 000000000..d4b7453a9 --- /dev/null +++ b/src/node/inet-socket-address.cc @@ -0,0 +1,106 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005 INRIA + * All rights reserved. + * + * 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: Mathieu Lacage + */ + +#include "inet-socket-address.h" +#include "ns3/assert.h" + +namespace ns3 { + +InetSocketAddress::InetSocketAddress (Ipv4Address ipv4, uint16_t port) + : m_ipv4 (ipv4), + m_port (port) +{} +InetSocketAddress::InetSocketAddress (Ipv4Address ipv4) + : m_ipv4 (ipv4), + m_port (0) +{} +InetSocketAddress::InetSocketAddress (const char *ipv4, uint16_t port) + : m_ipv4 (Ipv4Address (ipv4)), + m_port (port) +{} +InetSocketAddress::InetSocketAddress (const char * ipv4) + : m_ipv4 (Ipv4Address (ipv4)), + m_port (0) +{} +InetSocketAddress::InetSocketAddress (uint16_t port) + : m_ipv4 (Ipv4Address::GetAny ()), + m_port (port) +{} +uint16_t +InetSocketAddress::GetPort (void) const +{ + return m_port; +} +Ipv4Address +InetSocketAddress::GetIpv4 (void) const +{ + return m_ipv4; +} + +void +InetSocketAddress::SetPort (uint16_t port) +{ + m_port = port; +} +void +InetSocketAddress::SetIpv4 (Ipv4Address address) +{ + m_ipv4 = address; +} + +bool +InetSocketAddress::IsMatchingType (const Address &address) +{ + return address.CheckCompatible (GetType (), 6); +} + +InetSocketAddress::operator Address () const +{ + return ConvertTo (); +} + +Address +InetSocketAddress::ConvertTo (void) const +{ + uint8_t buf[6]; + m_ipv4.Serialize (buf); + buf[4] = m_port & 0xff; + buf[5] = (m_port >> 8) & 0xff; + return Address (GetType (), buf, 6); +} +InetSocketAddress +InetSocketAddress::ConvertFrom (const Address &address) +{ + NS_ASSERT (address.CheckCompatible (GetType (), 6)); + uint8_t buf[6]; + address.CopyTo (buf); + Ipv4Address ipv4 = Ipv4Address::Deserialize (buf); + uint16_t port = buf[4] | (buf[5] << 8); + return InetSocketAddress (ipv4, port); +} +uint8_t +InetSocketAddress::GetType (void) +{ + static uint8_t type = Address::Register (); + return type; +} + +} // namespace ns3 diff --git a/src/node/inet-socket-address.h b/src/node/inet-socket-address.h new file mode 100644 index 000000000..d6727342f --- /dev/null +++ b/src/node/inet-socket-address.h @@ -0,0 +1,117 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005 INRIA + * All rights reserved. + * + * 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: Mathieu Lacage + */ + +#ifndef INET_SOCKET_ADDRESS_H +#define INET_SOCKET_ADDRESS_H + +#include "address.h" +#include "ipv4-address.h" +#include + +namespace ns3 { + + +/** + * \brief an Inet address class + * + * This class is similar to inet_sockaddr in the BSD socket + * API. i.e., this class holds an Ipv4Address and a port number + * to form an ipv4 transport endpoint. + */ +class InetSocketAddress +{ +public: + /** + * \param ipv4 the ipv4 address + * \param port the port number + */ + InetSocketAddress (Ipv4Address ipv4, uint16_t port); + /** + * \param ipv4 the ipv4 address + * + * The port number is set to zero by default. + */ + InetSocketAddress (Ipv4Address ipv4); + /** + * \param port the port number + * + * The ipv4 address is set to the "Any" address by default. + */ + InetSocketAddress (uint16_t port); + /** + * \param ipv4 string which represents an ipv4 address + * \param port the port number + */ + InetSocketAddress (const char *ipv4, uint16_t port); + /** + * \param ipv4 string which represents an ipv4 address + * + * The port number is set to zero. + */ + InetSocketAddress (const char *ipv4); + /** + * \returns the port number + */ + uint16_t GetPort (void) const; + /** + * \returns the ipv4 address + */ + Ipv4Address GetIpv4 (void) const; + + /** + * \param port the new port number. + */ + void SetPort (uint16_t port); + /** + * \param address the new ipv4 address + */ + void SetIpv4 (Ipv4Address address); + + /** + * \returns true if the address matches, false otherwise. + */ + static bool IsMatchingType (const Address &address); + + /** + * \returns an Address instance which represents this + * InetSocketAddress instance. + */ + operator Address () const; + + /** + * \param address the Address instance to convert from. + * + * Returns an InetSocketAddress which corresponds to the input + * Address + */ + static InetSocketAddress ConvertFrom (const Address &address); +private: + Address ConvertTo (void) const; + + static uint8_t GetType (void); + Ipv4Address m_ipv4; + uint16_t m_port; +}; + +} // namespace ns3 + + +#endif /* INET_SOCKET_ADDRESS_H */ diff --git a/src/node/ipv4-address.cc b/src/node/ipv4-address.cc index c716930fd..4e1e344c5 100644 --- a/src/node/ipv4-address.cc +++ b/src/node/ipv4-address.cc @@ -19,11 +19,11 @@ * Author: Mathieu Lacage */ #include "ns3/debug.h" +#include "ipv4-address.h" +#include "ns3/assert.h" NS_DEBUG_COMPONENT_DEFINE("Ipv4Address"); -#include "ipv4-address.h" - namespace ns3 { @@ -135,6 +135,17 @@ Ipv4Address::Ipv4Address (char const *address) m_address = AsciiToIpv4Host (address); } +void +Ipv4Address::Set (uint32_t address) +{ + m_address = address; +} +void +Ipv4Address::Set (char const *address) +{ + m_address = AsciiToIpv4Host (address); +} + bool Ipv4Address::IsEqual (Ipv4Address other) const { @@ -160,8 +171,11 @@ Ipv4Address::IsBroadcast (void) const bool Ipv4Address::IsMulticast (void) const { - // XXX - return false; +// +// Multicast addresses are defined as ranging from 224.0.0.0 through +// 239.255.255.255 (which is E0000000 through EFFFFFFF in hex). +// + return (m_address >= 0xe0000000 && m_address <= 0xefffffff); } uint32_t @@ -182,6 +196,20 @@ Ipv4Address::Serialize (uint8_t buf[4]) const buf[2] = (m_address >> 8) & 0xff; buf[3] = (m_address >> 0) & 0xff; } +Ipv4Address +Ipv4Address::Deserialize (const uint8_t buf[4]) +{ + Ipv4Address ipv4; + ipv4.m_address = 0; + ipv4.m_address |= buf[0]; + ipv4.m_address <<= 8; + ipv4.m_address |= buf[1]; + ipv4.m_address <<= 8; + ipv4.m_address |= buf[2]; + ipv4.m_address <<= 8; + ipv4.m_address |= buf[3]; + return ipv4; +} void Ipv4Address::Print (std::ostream &os) const @@ -192,7 +220,39 @@ Ipv4Address::Print (std::ostream &os) const << ((m_address >> 0) & 0xff); } +bool +Ipv4Address::IsMatchingType (const Address &address) +{ + return address.CheckCompatible (GetType (), 4); +} +Ipv4Address::operator Address () +{ + return ConvertTo (); +} +Address +Ipv4Address::ConvertTo (void) const +{ + uint8_t buf[4]; + Serialize (buf); + return Address (GetType (), buf, 4); +} + +Ipv4Address +Ipv4Address::ConvertFrom (const Address &address) +{ + NS_ASSERT (address.CheckCompatible (GetType (), 4)); + uint8_t buf[4]; + address.CopyTo (buf); + return Deserialize (buf); +} + +uint8_t +Ipv4Address::GetType (void) +{ + static uint8_t type = Address::Register (); + return type; +} Ipv4Address Ipv4Address::GetZero (void) diff --git a/src/node/ipv4-address.h b/src/node/ipv4-address.h index cddfc176c..6589edcf9 100644 --- a/src/node/ipv4-address.h +++ b/src/node/ipv4-address.h @@ -24,6 +24,7 @@ #include #include +#include "address.h" namespace ns3 { @@ -51,6 +52,22 @@ public: */ Ipv4Address (char const *address); + /** + * input address is in host order. + * \param address The host order 32-bit address + */ + void Set (uint32_t address); + /** + * \brief Sets an Ipv4Address by parsing a the input C-string + * + * Input address is in format: + * hhh.xxx.xxx.lll + * where h is the high byte and l the + * low byte + * \param address C-string containing the address as described above + */ + void Set (char const *address); + /** * \brief Comparison operation between two Ipv4Addresses * \param other address to which to compare this address @@ -70,10 +87,18 @@ public: void SetHostOrder (uint32_t ip); /** * Serialize this address to a 4-byte buffer + * * \param buf output buffer to which this address gets overwritten with this * Ipv4Address */ void Serialize (uint8_t buf[4]) const; + /** + * \param buf buffer to read address from + * \returns an Ipv4Address + * + * The input address is expected to be in network byte order format. + */ + static Ipv4Address Deserialize (const uint8_t buf[4]); /** * \brief Print this address to the given output stream * @@ -91,15 +116,21 @@ public: * (bitwise and) with a network mask, yielding an IPv4 network * address. * - * \param a network mask + * \param mask a network mask */ Ipv4Address CombineMask (Ipv4Mask const &mask) const; + static bool IsMatchingType (const Address &address); + operator Address (); + static Ipv4Address ConvertFrom (const Address &address); + static Ipv4Address GetZero (void); static Ipv4Address GetAny (void); static Ipv4Address GetBroadcast (void); static Ipv4Address GetLoopback (void); private: + Address ConvertTo (void) const; + static uint8_t GetType (void); uint32_t m_address; }; diff --git a/src/node/llc-snap-header.cc b/src/node/llc-snap-header.cc index ee37ffecf..5bcb9714b 100644 --- a/src/node/llc-snap-header.cc +++ b/src/node/llc-snap-header.cc @@ -19,17 +19,24 @@ * Author: Mathieu Lacage */ -#include "ns3/assert.h" - #include "llc-snap-header.h" +#include "ns3/assert.h" +#include namespace ns3 { +NS_HEADER_ENSURE_REGISTERED (LlcSnapHeader); + +uint32_t +LlcSnapHeader::GetUid (void) +{ + static uint32_t uid = AllocateUid ("LlcSnapHeader.ns3"); + return uid; +} + LlcSnapHeader::LlcSnapHeader () {} -LlcSnapHeader::~LlcSnapHeader () -{} void LlcSnapHeader::SetType (uint16_t type) { @@ -48,13 +55,13 @@ LlcSnapHeader::GetSerializedSize (void) const } std::string -LlcSnapHeader::DoGetName (void) const +LlcSnapHeader::GetName (void) const { return "LLCSNAP"; } void -LlcSnapHeader::PrintTo (std::ostream &os) const +LlcSnapHeader::Print (std::ostream &os) const { os << "(type 0x"; os.setf (std::ios::hex, std::ios::basefield); @@ -64,7 +71,7 @@ LlcSnapHeader::PrintTo (std::ostream &os) const } void -LlcSnapHeader::SerializeTo (Buffer::Iterator start) const +LlcSnapHeader::Serialize (Buffer::Iterator start) const { Buffer::Iterator i = start; uint8_t buf[] = {0xaa, 0xaa, 0x03, 0, 0, 0}; @@ -72,7 +79,7 @@ LlcSnapHeader::SerializeTo (Buffer::Iterator start) const i.WriteHtonU16 (m_etherType); } uint32_t -LlcSnapHeader::DeserializeFrom (Buffer::Iterator start) +LlcSnapHeader::Deserialize (Buffer::Iterator start) { Buffer::Iterator i = start; i.Next (5+1); @@ -81,4 +88,4 @@ LlcSnapHeader::DeserializeFrom (Buffer::Iterator start) } -}; // namespace ns3 +} // namespace ns3 diff --git a/src/node/llc-snap-header.h b/src/node/llc-snap-header.h index ad98cda7c..cfc9a5d9c 100644 --- a/src/node/llc-snap-header.h +++ b/src/node/llc-snap-header.h @@ -23,28 +23,30 @@ #define LLC_SNAP_HEADER_H #include +#include #include "ns3/header.h" namespace ns3 { -class LlcSnapHeader : public Header { - public: - LlcSnapHeader (); - virtual ~LlcSnapHeader (); +class LlcSnapHeader : public Header +{ +public: + static uint32_t GetUid (void); + LlcSnapHeader (); void SetType (uint16_t type); uint16_t GetType (void); + std::string GetName (void) const; + void Print (std::ostream &os) const; + uint32_t GetSerializedSize (void) const; + void Serialize (Buffer::Iterator start) const; + uint32_t Deserialize (Buffer::Iterator start); private: - virtual std::string DoGetName (void) const; - virtual void PrintTo (std::ostream &os) const; - virtual uint32_t GetSerializedSize (void) const; - virtual void SerializeTo (Buffer::Iterator start) const; - virtual uint32_t DeserializeFrom (Buffer::Iterator start); uint16_t m_etherType; }; -}; // namespace ns3 +} // namespace ns3 #endif /* LLC_SNAP_HEADER_H */ diff --git a/src/node/mac-address.cc b/src/node/mac-address.cc deleted file mode 100644 index 9dcf9bdd6..000000000 --- a/src/node/mac-address.cc +++ /dev/null @@ -1,210 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2005 INRIA - * All rights reserved. - * - * 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: Mathieu Lacage - */ - -#include -#include -#include "ns3/assert.h" -#include "mac-address.h" - -#define ASCII_a (0x41) -#define ASCII_z (0x5a) -#define ASCII_A (0x61) -#define ASCII_Z (0x7a) -#define ASCII_COLON (0x3a) -#define ASCII_ZERO (0x30) - -namespace ns3 { - -// Static variables -uint8_t MacAddress::g_nextAddress[MacAddress::MAX_LEN]; - -static char -AsciiToLowCase (char c) -{ - if (c >= ASCII_a && c <= ASCII_z) { - return c; - } else if (c >= ASCII_A && c <= ASCII_Z) { - return c + (ASCII_a - ASCII_A); - } else { - return c; - } -} - - -MacAddress::MacAddress () : m_len(0) -{ - for (int i=0; i < MacAddress::MAX_LEN; i++) - { - m_address[i] = 0; - } -} - -MacAddress::MacAddress(uint8_t len) : m_len(len) -{ - NS_ASSERT (len <= MacAddress::MAX_LEN); - AdvanceAddress(); - memcpy(m_address, g_nextAddress, len); -} - -MacAddress::MacAddress (uint8_t const *address, uint8_t len) -{ - NS_ASSERT (len <= MacAddress::MAX_LEN); - for (int i=0; i < len; i++) - { - m_address[i] = address[i]; - } - for (int i=len; i < MacAddress::MAX_LEN; i++) - { - m_address[i] = 0; - } - m_len = len; -} - -MacAddress::MacAddress (char const *str) -{ - int i = 0; - while (*str != 0 && i < MacAddress::MAX_LEN) { - uint8_t byte = 0; - while (*str != ASCII_COLON && *str != 0) { - byte <<= 4; - char low = AsciiToLowCase (*str); - if (low >= ASCII_a) { - byte |= low - ASCII_a + 10; - } else { - byte |= low - ASCII_ZERO; - } - str++; - } - m_address[i] = byte; - i++; - if (*str == 0) { - break; - } - str++; - } - m_len = i; -} - -MacAddress::~MacAddress () -{} - -bool -MacAddress::IsEqual (MacAddress other) const -{ - if (memcmp(other.m_address, m_address, m_len)) - { - return false; - } - else - { - return true; - } -} - -void -MacAddress::Print (std::ostream &os) const -{ - int i; - if (m_len == 0) - { - os << "NULL-ADDRESS"; - return; - } - os.setf (std::ios::hex, std::ios::basefield); - std::cout.fill('0'); - for (i=0; i< (m_len-1); i++) - { - os << std::setw(2) << (uint32_t)m_address[i] << ":"; - } - // Final byte not suffixed by ":" - os << std::setw(2) << (uint32_t)m_address[i]; - os.setf (std::ios::dec, std::ios::basefield); - std::cout.fill(' '); -} - -uint8_t -MacAddress::GetLength () const -{ - return m_len; -} - -void -MacAddress::Peek (uint8_t ad[MacAddress::MAX_LEN]) const -{ - memcpy (ad, m_address, MacAddress::MAX_LEN); -} -void -MacAddress::Set (uint8_t const ad[MacAddress::MAX_LEN], uint8_t len) -{ - memcpy (m_address, ad, MacAddress::MAX_LEN); - m_len = len; -} - -// Static methods -void MacAddress::AdvanceAddress() - { - // Advance to next address, little end first - for(size_t i = 0; i < MAX_LEN; ++i) - { - if (++g_nextAddress[i] != 0) break; - } - } - -// Non-member operators -bool operator == (MacAddress const&a, MacAddress const&b) -{ - return a.IsEqual (b); -} - -bool operator != (MacAddress const&a, MacAddress const&b) -{ - return !a.IsEqual (b); -} - -bool operator < (MacAddress const&a, MacAddress const&b) -{ - uint8_t a_p[MacAddress::MAX_LEN]; - uint8_t b_p[MacAddress::MAX_LEN]; - a.Peek (a_p); - b.Peek (b_p); - NS_ASSERT (a.GetLength() == b.GetLength()); - for (uint8_t i = 0; i < a.GetLength(); i++) - { - if (a_p[i] < b_p[i]) - { - return true; - } - else if (a_p[i] > b_p[i]) - { - return false; - } - } - return false; -} - -std::ostream& operator<< (std::ostream& os, MacAddress const& address) -{ - address.Print (os); - return os; -} - - -}; // namespace ns3 diff --git a/src/node/mac-address.h b/src/node/mac-address.h deleted file mode 100644 index 76c5a6ae0..000000000 --- a/src/node/mac-address.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2005 INRIA - * All rights reserved. - * - * 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: Mathieu Lacage - */ - -#ifndef MAC_ADDRESS_H -#define MAC_ADDRESS_H - -#include -#include - -namespace ns3 { - -/** - * \brief base class for Network Device addresses - * - * This class is a base class for different types of Network - * Device addresses. It generically stores an address of - * MAX_ADDR_LEN bytes, and provides methods to compare, print, and set - * the address. - */ -class MacAddress { -public: - enum { - MAX_LEN = 32 - }; - /** - * \brief Construct a null MacAddress - * - * This MacAddress has length of zero, and is internally all zeros - */ - MacAddress (void); - /** - * \brief Construct a MacAddress using the next available - * address. - * \see MacAddres::Next - * \param len length, in bytes, of the desired address - */ - MacAddress(uint8_t len); - /** - * \brief Construct a MacAddress from a byte-array - * - * low byte should be first. - * \param address a byte array indicating the address - * \param len length, in bytes, of the address points to - */ - MacAddress (uint8_t const *address, uint8_t len); - /** - * \brief Construct a MacAddress from a C-string - * - * The string should look like this: - * hh:xx:xx:xx:xx:ll - * where hh is the high byte and ll is - * the low byte. - * \param address the C-string representation of the address - */ - MacAddress (char const *address); - ~MacAddress (); - - /** - * \brief Comparison operation between MacAddresses - * \param other The address against which to compare this one - * \return True if they are equal, false otherwise. - */ - bool IsEqual (MacAddress other) const; - /** - * \brief Print this MacAddress to a stream - * - * The format is colon seperated groups of two hexadecimal digits - * \param os The output stream desired - */ - void Print (std::ostream &os) const; - - /** - * \return The length in bytes of this MacAddress - */ - uint8_t GetLength() const; - /** - * \brief Copy routine to peek the contents of the MacAddress - * - * \param ad Output parameter which holds a copy of this MacAddress - */ - void Peek (uint8_t ad[MAX_LEN]) const; - /** - * \brief Sets this MacAddress to a specific value - * \param ad byte buffer to set the MacAddress to - * \param len the length of the buffer - */ - void Set (uint8_t const ad[MAX_LEN], uint8_t len); - - // Static methods/members - /** - * - * Advance the global to the next available mac address. - */ - static void AdvanceAddress(); - static uint8_t g_nextAddress[MAX_LEN]; - -private: - uint8_t m_address[MAX_LEN]; - uint8_t m_len; -}; - -bool operator == (MacAddress const&a, MacAddress const&b); -bool operator != (MacAddress const&a, MacAddress const&b); -bool operator < (MacAddress const&a, MacAddress const&b); - -std::ostream& operator<< (std::ostream& os, MacAddress const& address); - -}; // namespace ns3 - -#endif /* MAC_ADDRESS_H */ diff --git a/src/node/net-device.cc b/src/node/net-device.cc index 7bc61dc9b..e3b9537aa 100644 --- a/src/node/net-device.cc +++ b/src/node/net-device.cc @@ -35,7 +35,7 @@ namespace ns3 { const InterfaceId NetDevice::iid = MakeInterfaceId ("NetDevice", Object::iid); -NetDevice::NetDevice(Ptr node, const MacAddress& addr) : +NetDevice::NetDevice(Ptr node, const Address& addr) : m_node (node), m_name(""), m_ifIndex (0), @@ -53,7 +53,7 @@ NetDevice::NetDevice(Ptr node, const MacAddress& addr) : NetDevice::~NetDevice () {} -MacAddress +Address NetDevice::GetAddress (void) const { return m_address; @@ -113,7 +113,7 @@ NetDevice::IsBroadcast (void) const { return m_isBroadcast; } -MacAddress const & +Address const & NetDevice::GetBroadcast (void) const { NS_ASSERT (m_isBroadcast); @@ -121,7 +121,7 @@ NetDevice::GetBroadcast (void) const } void -NetDevice::EnableBroadcast (MacAddress broadcast) +NetDevice::EnableBroadcast (Address broadcast) { m_isBroadcast = true; m_broadcast = broadcast; @@ -171,7 +171,7 @@ NetDevice::DisablePointToPoint (void) // Receive packet from above bool -NetDevice::Send(Packet& p, const MacAddress& dest, uint16_t protocolNumber) +NetDevice::Send(const Packet& p, const Address& dest, uint16_t protocolNumber) { if (m_isUp) { @@ -197,18 +197,19 @@ NetDevice::GetChannel (void) const // Receive packets from below bool -NetDevice::ForwardUp(Packet& p, uint32_t param) +NetDevice::ForwardUp(const Packet& p, uint16_t param, const Address &from) { bool retval = false; - Packet packet = p; - NS_DEBUG ("NetDevice::ForwardUp: UID is " << packet.GetUid() + NS_DEBUG ("NetDevice::ForwardUp: UID is " << p.GetUid() << " device is: " << GetName()); if (!m_receiveCallback.IsNull ()) { - retval = m_receiveCallback (this, packet, param); - } else { + retval = m_receiveCallback (this, p, param, from); + } + else + { NS_DEBUG ("NetDevice::Receive call back is NULL"); } @@ -248,7 +249,7 @@ NetDevice::NeedsArp (void) const } void -NetDevice::SetReceiveCallback (Callback,const Packet &,uint16_t> cb) +NetDevice::SetReceiveCallback (ReceiveCallback cb) { m_receiveCallback = cb; } diff --git a/src/node/net-device.h b/src/node/net-device.h index b5de34fc8..cc93e6420 100644 --- a/src/node/net-device.h +++ b/src/node/net-device.h @@ -28,7 +28,7 @@ #include "ns3/packet.h" #include "ns3/object.h" #include "ns3/ptr.h" -#include "mac-address.h" +#include "address.h" namespace ns3 { @@ -79,9 +79,9 @@ public: Ptr GetChannel (void) const; /** - * \return the current MacAddress of this interface. + * \return the current Address of this interface. */ - MacAddress GetAddress (void) const; + Address GetAddress (void) const; /** * \param mtu MTU value, in bytes, to set for the device * \return whether the MTU value was within legal bounds @@ -137,7 +137,7 @@ public: * Calling this method is invalid if IsBroadcast returns * not true. */ - MacAddress const &GetBroadcast (void) const; + Address const &GetBroadcast (void) const; /** * \return value of m_isMulticast flag */ @@ -154,11 +154,11 @@ public: * is received. * * Called from higher layer to send packet into Network Device - * to the specified destination MacAddress + * to the specified destination Address * * \return whether the Send operation succeeded */ - bool Send(Packet& p, const MacAddress& dest, uint16_t protocolNumber); + bool Send(const Packet& p, const Address& dest, uint16_t protocolNumber); /** * \returns the node base class which contains this network * interface. @@ -177,23 +177,36 @@ public: */ bool NeedsArp (void) const; + /** + * \param device a pointer to the net device which is calling this callback + * \param packet the packet received + * \param protocol the 16 bit protocol number associated with this packet. + * This protocol number is expected to be the same protocol number + * given to the Send method by the user on the sender side. + * \param address the address of the sender + * \returns true if the callback could handle the packet successfully, false + * otherwise. + */ + typedef Callback,const Packet &,uint16_t,const Address &> ReceiveCallback; + /** * \param cb callback to invoke whenever a packet has been received and must * be forwarded to the higher layers. + * */ - void SetReceiveCallback (Callback,const Packet &,uint16_t> cb); + void SetReceiveCallback (ReceiveCallback cb); protected: /** * \param node base class node pointer of device's node * \param addr MAC address of this device. */ - NetDevice(Ptr node, const MacAddress& addr); + NetDevice(Ptr node, const Address& addr); /** * Enable broadcast support. This method should be * called by subclasses from their constructor */ - void EnableBroadcast (MacAddress broadcast); + void EnableBroadcast (Address broadcast); /** * Set m_isBroadcast flag to false */ @@ -230,6 +243,7 @@ public: * \param p packet sent from below up to Network Device * \param param Extra parameter extracted from header and needed by * some protocols + * \param address the address of the sender of this packet. * \returns true if the packet was forwarded successfully, * false otherwise. * @@ -237,7 +251,7 @@ public: * forwards it to the higher layers by calling this method * which is responsible for passing it up to the Rx callback. */ - bool ForwardUp (Packet& p, uint32_t param); + bool ForwardUp (const Packet& p, uint16_t param, const Address &address); /** @@ -248,8 +262,6 @@ public: */ virtual void DoDispose (void); - Callback,const Packet &,uint16_t> m_receiveCallback; - private: /** * \param p packet to send @@ -262,7 +274,7 @@ public: * method. When the link is Up, this method is invoked to ask * subclasses to forward packets. Subclasses MUST override this method. */ - virtual bool SendTo (Packet& p, const MacAddress &dest, uint16_t protocolNumber) = 0; + virtual bool SendTo (const Packet& p, const Address &dest, uint16_t protocolNumber) = 0; /** * \returns true if this NetDevice needs the higher-layers * to perform ARP over it, false otherwise. @@ -289,14 +301,15 @@ public: Ptr m_node; std::string m_name; uint16_t m_ifIndex; - MacAddress m_address; - MacAddress m_broadcast; + Address m_address; + Address m_broadcast; uint16_t m_mtu; bool m_isUp; bool m_isBroadcast; bool m_isMulticast; bool m_isPointToPoint; Callback m_linkChangeCallback; + ReceiveCallback m_receiveCallback; }; }; // namespace ns3 diff --git a/src/node/node-list.cc b/src/node/node-list.cc index 4fed7eb5e..eb43727d9 100644 --- a/src/node/node-list.cc +++ b/src/node/node-list.cc @@ -40,6 +40,30 @@ public: namespace ns3 { +NodeListIndex::NodeListIndex () + : m_index (0) +{} +NodeListIndex::NodeListIndex (uint32_t index) + : m_index (index) +{} +void +NodeListIndex::Print (std::ostream &os) +{ + os << "nodeid=" << m_index; +} +uint16_t +NodeListIndex::GetUid (void) +{ + static uint16_t uid = AllocateUid ("NodeListIndex"); + return uid; +} +uint32_t +NodeListIndex::Get (void) const +{ + return m_index; +} + + /** * The private node list used by the static-based API */ @@ -53,7 +77,6 @@ public: NodeList::Iterator Begin (void); NodeList::Iterator End (void); TraceResolver *CreateTraceResolver (TraceContext const &context); - Node *PeekNode (uint32_t n); Ptr GetNode (uint32_t n); uint32_t GetNNodes (void); @@ -99,11 +122,6 @@ NodeListPriv::GetNNodes (void) { return m_nodes.size (); } -Node * -NodeListPriv::PeekNode (uint32_t n) -{ - return PeekPointer (m_nodes[n]); -} Ptr NodeListPriv::GetNode (uint32_t n) @@ -115,11 +133,11 @@ NodeListPriv::GetNode (uint32_t n) TraceResolver * NodeListPriv::CreateTraceResolver (TraceContext const &context) { - ArrayTraceResolver *resolver = - new ArrayTraceResolver + ArrayTraceResolver, NodeListIndex> *resolver = + new ArrayTraceResolver, NodeListIndex> (context, MakeCallback (&NodeListPriv::GetNNodes, this), - MakeCallback (&NodeListPriv::PeekNode, this)); + MakeCallback (&NodeListPriv::GetNode, this)); return resolver; } diff --git a/src/node/node-list.h b/src/node/node-list.h index 607a47d71..1fdcd9e0e 100644 --- a/src/node/node-list.h +++ b/src/node/node-list.h @@ -23,7 +23,7 @@ #define NODE_LIST_H #include -#include "ns3/array-trace-resolver.h" +#include "ns3/trace-context-element.h" #include "ns3/ptr.h" namespace ns3 { @@ -32,6 +32,19 @@ class Node; class TraceResolver; class TraceContext; +class NodeListIndex : public TraceContextElement +{ +public: + NodeListIndex (); + NodeListIndex (uint32_t index); + void Print (std::ostream &os); + static uint16_t GetUid (void); + uint32_t Get (void) const; +private: + uint32_t m_index; +}; + + /** * \brief the list of simulation nodes. * @@ -40,7 +53,6 @@ class TraceContext; class NodeList { public: - typedef ArrayTraceResolver::Index NodeIndex; typedef std::vector< Ptr >::iterator Iterator; /** diff --git a/src/node/node.cc b/src/node/node.cc index fa33ff7df..ee83e8588 100644 --- a/src/node/node.cc +++ b/src/node/node.cc @@ -1,51 +1,82 @@ -// -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- -// -// Copyright (c) 2006 Georgia Tech Research Corporation -// All rights reserved. -// -// 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: George F. Riley -// - -// Implement the basic Node object for ns3. -// George F. Riley, Georgia Tech, Fall 2006 - +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 Georgia Tech Research Corporation, INRIA + * + * 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 + * + * Authors: George F. Riley + * Mathieu Lacage + */ #include "node.h" #include "node-list.h" #include "net-device.h" #include "application.h" +#include "packet-socket-factory.h" #include "ns3/simulator.h" +#include "ns3/composite-trace-resolver.h" +#include "ns3/array-trace-resolver.h" namespace ns3{ const InterfaceId Node::iid = MakeInterfaceId ("Node", Object::iid); +NodeNetDeviceIndex::NodeNetDeviceIndex () + : m_index (0) +{} +NodeNetDeviceIndex::NodeNetDeviceIndex (uint32_t index) + : m_index (index) +{} +uint32_t +NodeNetDeviceIndex::Get (void) const +{ + return m_index; +} +void +NodeNetDeviceIndex::Print (std::ostream &os) const +{ + os << "device=" << m_index; +} +uint16_t +NodeNetDeviceIndex::GetUid (void) +{ + static uint16_t uid = AllocateUid ("NodeNetDeviceIndex"); + return uid; +} + + + Node::Node() : m_id(0), m_sid(0) { - SetInterfaceId (Node::iid); - m_id = NodeList::Add (this); + Construct (); } Node::Node(uint32_t sid) : m_id(0), m_sid(sid) { + Construct (); +} + +void +Node::Construct (void) +{ SetInterfaceId (Node::iid); m_id = NodeList::Add (this); + Ptr socketFactory = Create (); + AddInterface (socketFactory); } Node::~Node () @@ -54,7 +85,9 @@ Node::~Node () TraceResolver * Node::CreateTraceResolver (TraceContext const &context) { - return DoCreateTraceResolver (context); + CompositeTraceResolver *resolver = new CompositeTraceResolver (context); + DoFillTraceResolver (*resolver); + return resolver; } uint32_t @@ -74,8 +107,9 @@ Node::AddDevice (Ptr device) { uint32_t index = m_devices.size (); m_devices.push_back (device); - DoAddDevice (device); device->SetIfIndex(index); + device->SetReceiveCallback (MakeCallback (&Node::ReceiveFromDevice, this)); + NotifyDeviceAdded (device); return index; } Ptr @@ -107,8 +141,27 @@ Node::GetNApplications (void) const return m_applications.size (); } +TraceResolver * +Node::CreateDevicesTraceResolver (const TraceContext &context) +{ + ArrayTraceResolver,NodeNetDeviceIndex> *resolver = + new ArrayTraceResolver,NodeNetDeviceIndex> + (context, + MakeCallback (&Node::GetNDevices, this), + MakeCallback (&Node::GetDevice, this)); + + return resolver; +} -void Node::DoDispose() +void +Node::DoFillTraceResolver (CompositeTraceResolver &resolver) +{ + resolver.Add ("devices", + MakeCallback (&Node::CreateDevicesTraceResolver, this)); +} + +void +Node::DoDispose() { for (std::vector >::iterator i = m_devices.begin (); i != m_devices.end (); i++) @@ -129,4 +182,56 @@ void Node::DoDispose() Object::DoDispose (); } +void +Node::NotifyDeviceAdded (Ptr device) +{} + +void +Node::RegisterProtocolHandler (ProtocolHandler handler, + uint16_t protocolType, + Ptr device) +{ + struct Node::ProtocolHandlerEntry entry; + entry.handler = handler; + entry.protocol = protocolType; + entry.device = device; + m_handlers.push_back (entry); +} + +void +Node::UnregisterProtocolHandler (ProtocolHandler handler) +{ + for (ProtocolHandlerList::iterator i = m_handlers.begin (); + i != m_handlers.end (); i++) + { + if (i->handler.IsEqual (handler)) + { + m_handlers.erase (i); + break; + } + } +} + +bool +Node::ReceiveFromDevice (Ptr device, const Packet &packet, + uint16_t protocol, const Address &from) +{ + bool found = false; + for (ProtocolHandlerList::iterator i = m_handlers.begin (); + i != m_handlers.end (); i++) + { + if (i->device == 0 || + (i->device != 0 && i->device == device)) + { + if (i->protocol == 0 || + i->protocol == protocol) + { + i->handler (device, packet, protocol, from); + found = true; + } + } + } + return found; +} + }//namespace ns3 diff --git a/src/node/node.h b/src/node/node.h index 4e4e7b8c7..f4d898f53 100644 --- a/src/node/node.h +++ b/src/node/node.h @@ -1,33 +1,31 @@ -// -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- -// -// Copyright (c) 2006 Georgia Tech Research Corporation -// All rights reserved. -// -// 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: George F. Riley -// - -// Define the basic Node object for ns3. -// George F. Riley, Georgia Tech, Fall 2006 - -#ifndef I_NODE_H -#define I_NODE_H +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 Georgia Tech Research Corporation, INRIA + * + * 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 + * + * Authors: George F. Riley + * Mathieu Lacage + */ +#ifndef NODE_H +#define NODE_H #include #include "ns3/object.h" +#include "ns3/callback.h" +#include "ns3/trace-context-element.h" namespace ns3 { @@ -35,6 +33,21 @@ class TraceContext; class TraceResolver; class NetDevice; class Application; +class Packet; +class Address; +class CompositeTraceResolver; + +class NodeNetDeviceIndex : public TraceContextElement +{ +public: + NodeNetDeviceIndex (); + NodeNetDeviceIndex (uint32_t index); + uint32_t Get (void) const; + void Print (std::ostream &os) const; + static uint16_t GetUid (void); +private: + uint32_t m_index; +}; /** * \brief A network Node. @@ -58,6 +71,17 @@ class Node : public Object public: static const InterfaceId iid; + /** + * Must be invoked by subclasses only. + */ + Node(); + /** + * \param systemId a unique integer used for parallel simulations. + * + * Must be invoked by subclasses only. + */ + Node(uint32_t systemId); + virtual ~Node(); /** @@ -93,11 +117,15 @@ public: * Associate this device to this node. * This method is called automatically from NetDevice::NetDevice * so the user has little reason to call this method himself. + * The index returned is always non-zero. */ uint32_t AddDevice (Ptr device); /** * \param index the index of the requested NetDevice * \returns the requested NetDevice associated to this Node. + * + * The indexes used by the GetDevice method start at one and + * end at GetNDevices () */ Ptr GetDevice (uint32_t index) const; /** @@ -127,31 +155,51 @@ public: */ uint32_t GetNApplications (void) const; -protected: /** - * Must be invoked by subclasses only. + * A protocol handler */ - Node(); + typedef Callback, const Packet &,uint16_t,const Address &> ProtocolHandler; /** - * \param systemId a unique integer used for parallel simulations. + * \param handler the handler to register + * \param protocolType the type of protocol this handler is + * interested in. This protocol type is a so-called + * EtherType, as registered here: + * http://standards.ieee.org/regauth/ethertype/eth.txt + * the value zero is interpreted as matching all + * protocols. + * \param device the device attached to this handler. If the + * value is zero, the handler is attached to all + * devices on this node. + */ + void RegisterProtocolHandler (ProtocolHandler handler, + uint16_t protocolType, + Ptr device); + /** + * \param handler the handler to unregister * - * Must be invoked by subclasses only. + * After this call returns, the input handler will never + * be invoked anymore. */ - Node(uint32_t systemId); + void UnregisterProtocolHandler (ProtocolHandler handler); + +protected: /** * The dispose method. Subclasses must override this method * and must chain up to it by calling Node::DoDispose at the * end of their own DoDispose method. */ virtual void DoDispose (void); -private: /** - * \param context the trace context - * \returns a trace resolver to the user. The user must delete it. + * \param resolver the resolver to store trace sources in. * - * Subclasses must implement this method. + * If a subclass wants to add new traces to a Node, it needs + * to override this method and record the new trace sources + * in the input resolver. Subclasses also _must_ chain up to + * their parent's DoFillTraceResolver method prior + * to recording they own trace sources. */ - virtual TraceResolver *DoCreateTraceResolver (TraceContext const &context) = 0; + virtual void DoFillTraceResolver (CompositeTraceResolver &resolver); +private: /** * \param device the device added to this Node. * @@ -160,14 +208,26 @@ private: * at this point to setup the node's receive function for * the NetDevice packets. */ - virtual void DoAddDevice (Ptr device) = 0; + virtual void NotifyDeviceAdded (Ptr device); + bool ReceiveFromDevice (Ptr device, const Packet &packet, + uint16_t protocol, const Address &from); + void Construct (void); + TraceResolver *CreateDevicesTraceResolver (const TraceContext &context); + + struct ProtocolHandlerEntry { + ProtocolHandler handler; + uint16_t protocol; + Ptr device; + }; + typedef std::vector ProtocolHandlerList; uint32_t m_id; // Node id for this node uint32_t m_sid; // System id for this node std::vector > m_devices; std::vector > m_applications; + ProtocolHandlerList m_handlers; }; } //namespace ns3 -#endif /* I_NODE_H */ +#endif /* NODE_H */ diff --git a/src/node/packet-socket-address.cc b/src/node/packet-socket-address.cc new file mode 100644 index 000000000..1b16b9cb5 --- /dev/null +++ b/src/node/packet-socket-address.cc @@ -0,0 +1,134 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 INRIA + * + * 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: Mathieu Lacage + */ +#include "packet-socket-address.h" +#include "net-device.h" + +namespace ns3 { + +PacketSocketAddress::PacketSocketAddress () +{} +void +PacketSocketAddress::SetProtocol (uint16_t protocol) +{ + m_protocol = protocol; +} +void +PacketSocketAddress::SetAllDevices (void) +{ + m_isSingleDevice = false; + m_device = 0; +} +void +PacketSocketAddress::SetSingleDevice (uint32_t index) +{ + m_isSingleDevice = true; + m_device = index; +} +void +PacketSocketAddress::SetPhysicalAddress (const Address address) +{ + m_address = address; +} + +uint16_t +PacketSocketAddress::GetProtocol (void) const +{ + return m_protocol; +} +bool +PacketSocketAddress::IsSingleDevice (void) const +{ + return m_isSingleDevice; +} +uint32_t +PacketSocketAddress::GetSingleDevice (void) const +{ + return m_device; +} +Address +PacketSocketAddress::GetPhysicalAddress (void) const +{ + return m_address; +} + +PacketSocketAddress::operator Address () const +{ + return ConvertTo (); +} + +Address +PacketSocketAddress::ConvertTo (void) const +{ + Address address; + uint8_t buffer[Address::MAX_SIZE]; + buffer[0] = m_protocol & 0xff; + buffer[1] = (m_protocol >> 8) & 0xff; + buffer[2] = (m_device >> 24) & 0xff; + buffer[3] = (m_device >> 16) & 0xff; + buffer[4] = (m_device >> 8) & 0xff; + buffer[5] = (m_device >> 0) & 0xff; + buffer[6] = m_isSingleDevice?1:0; + uint32_t copied = m_address.CopyAllTo (buffer + 7, Address::MAX_SIZE - 7); + return Address (GetType (), buffer, 7 + copied); +} +PacketSocketAddress +PacketSocketAddress::ConvertFrom (const Address &address) +{ + NS_ASSERT (IsMatchingType (address)); + uint8_t buffer[Address::MAX_SIZE]; + address.CopyTo (buffer); + uint16_t protocol = buffer[0] | (buffer[1] << 8); + uint32_t device = 0; + device |= buffer[2]; + device <<= 8; + device |= buffer[3]; + device <<= 8; + device |= buffer[4]; + device <<= 8; + device |= buffer[5]; + bool isSingleDevice = (buffer[6] == 1)?true:false; + Address physical; + physical.CopyAllFrom (buffer + 7, Address::MAX_SIZE - 7); + PacketSocketAddress ad; + ad.SetProtocol (protocol); + if (isSingleDevice) + { + ad.SetSingleDevice (device); + } + else + { + ad.SetAllDevices (); + } + ad.SetPhysicalAddress (physical); + return ad; +} +bool +PacketSocketAddress::IsMatchingType (const Address &address) +{ + return address.IsMatchingType (GetType ()); +} +uint8_t +PacketSocketAddress::GetType (void) +{ + static uint8_t type = Address::Register (); + return type; +} + +} // namespace ns3 diff --git a/src/node/packet-socket-address.h b/src/node/packet-socket-address.h new file mode 100644 index 000000000..e280db8df --- /dev/null +++ b/src/node/packet-socket-address.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 INRIA + * + * 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: Mathieu Lacage + */ +#ifndef PACKET_SOCKET_ADDRESS_H +#define PACKET_SOCKET_ADDRESS_H + +#include "ns3/ptr.h" +#include "address.h" +#include "eui48-address.h" +#include "eui64-address.h" +#include "net-device.h" + +namespace ns3 { + +class NetDevice; + +class PacketSocketAddress +{ + public: + PacketSocketAddress (); + void SetProtocol (uint16_t protocol); + + void SetAllDevices (void); + void SetSingleDevice (uint32_t device); + void SetPhysicalAddress (const Address address); + + uint16_t GetProtocol (void) const; + uint32_t GetSingleDevice (void) const; + bool IsSingleDevice (void) const; + Address GetPhysicalAddress (void) const; + + /** + * \returns a new Address instance + * + * Convert an instance of this class to a polymorphic Address instance. + */ + operator Address () const; + /** + * \param address a polymorphic address + * + * Convert a polymorphic address to an Eui48Address instance. + * The conversion performs a type check. + */ + static PacketSocketAddress ConvertFrom (const Address &address); + /** + * \returns true if the address matches, false otherwise. + */ + static bool IsMatchingType (const Address &address); + private: + static uint8_t GetType (void); + Address ConvertTo (void) const; + uint16_t m_protocol; + bool m_isSingleDevice; + uint32_t m_device; + Address m_address; +}; + + +} // namespace ns3 + +#endif /* PACKET_SOCKET_ADDRESS_H */ diff --git a/src/common/header.cc b/src/node/packet-socket-factory.cc similarity index 55% rename from src/common/header.cc rename to src/node/packet-socket-factory.cc index dae8acb4b..3cd403a1a 100644 --- a/src/common/header.cc +++ b/src/node/packet-socket-factory.cc @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2005 INRIA + * Copyright (c) 2007 Emmanuelle Laprise * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -16,14 +16,25 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * Author: Mathieu Lacage + * Author: Emmanuelle Laprise */ - -#include "header.h" +#include "packet-socket-factory.h" +#include "node.h" namespace ns3 { -Header::~Header () -{} +const InterfaceId PacketSocketFactory::iid = MakeInterfaceId ("Packet", + SocketFactory::iid); -}; // namespace ns3 +PacketSocketFactory::PacketSocketFactory () +{ + SetInterfaceId (PacketSocketFactory::iid); +} + +Ptr PacketSocketFactory::CreateSocket (void) +{ + Ptr node = QueryInterface (Node::iid); + Ptr socket = Create (node); + return socket; +} +} // namespace ns3 diff --git a/src/internet-node/arp-private.h b/src/node/packet-socket-factory.h similarity index 53% rename from src/internet-node/arp-private.h rename to src/node/packet-socket-factory.h index 6e238ade0..0f052ce6e 100644 --- a/src/internet-node/arp-private.h +++ b/src/node/packet-socket-factory.h @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2007 INRIA + * Copyright (c) 2007 Emmanuelle Laprise * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -16,36 +16,37 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * Author: Mathieu Lacage + * Author: Emmanuelle Laprise */ -#ifndef ARP_PRIVATE_H -#define ARP_PRIVATE_H +#ifndef PACKET_SOCKET_FACTORY_H +#define PACKET_SOCKET_FACTORY_H -#include "ns3/object.h" -#include "ns3/ipv4-address.h" +#include "socket-factory.h" +#include "packet-socket.h" namespace ns3 { -class NetDevice; -class MacAddress; -class Packet; -class ArpL3Protocol; +class Socket; -class ArpPrivate : public Object +/** + * This can be used as an interface in a node in order for the node to + * generate PacketSockets that can connect to net devices. + */ +class PacketSocketFactory : public SocketFactory { public: - static const InterfaceId iid; - ArpPrivate (Ptr arp); - virtual ~ArpPrivate (); - bool Lookup (Packet &p, Ipv4Address destination, - Ptr device, - MacAddress *hardwareDestination); -protected: - virtual void DoDispose (void); -private: - Ptr m_arp; + static const InterfaceId iid; /// Interface identifier + + PacketSocketFactory (); + + /** + * Creates a PacketSocket and returns a pointer to it. + * + * \return a pointer to the created socket + */ + virtual Ptr CreateSocket (void); }; } // namespace ns3 -#endif /* ARP_PRIVATE_H */ +#endif /* PACKET_SOCKET_FACTORY_H */ diff --git a/src/node/packet-socket.cc b/src/node/packet-socket.cc new file mode 100644 index 000000000..f642eee9e --- /dev/null +++ b/src/node/packet-socket.cc @@ -0,0 +1,285 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 Emmanuelle Laprise, INRIA + * + * 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 + * + * Authors: Emmanuelle Laprise + * Mathieu Lacage + */ + +#include "packet-socket.h" +#include "packet-socket-address.h" +#include "ns3/debug.h" +#include "ns3/node.h" + +NS_DEBUG_COMPONENT_DEFINE ("PacketSocket"); + +namespace ns3 { + +PacketSocket::PacketSocket (Ptr node) + : m_node (node) +{ + Init(); +} + +void +PacketSocket::Init() +{ + m_state = STATE_OPEN; + m_shutdownSend = false; + m_shutdownRecv = false; + m_errno = ERROR_NOTERROR; +} + +PacketSocket::~PacketSocket () +{} + +void +PacketSocket::DoDispose (void) +{ + m_device = 0; +} + +enum Socket::SocketErrno +PacketSocket::GetErrno (void) const +{ + return m_errno; +} + +Ptr +PacketSocket::GetNode (void) const +{ + return m_node; +} + +int +PacketSocket::Bind (void) +{ + PacketSocketAddress address; + address.SetProtocol (0); + address.SetAllDevices (); + return DoBind (address); +} +int +PacketSocket::Bind (const Address &address) +{ + if (!PacketSocketAddress::IsMatchingType (address)) + { + m_errno = ERROR_INVAL; + return -1; + } + PacketSocketAddress ad = PacketSocketAddress::ConvertFrom (address); + return DoBind (ad); +} + +int +PacketSocket::DoBind (const PacketSocketAddress &address) +{ + if (m_state == STATE_BOUND || + m_state == STATE_CONNECTED) + { + m_errno = ERROR_INVAL; + return -1; + } + if (m_state == STATE_CLOSED) + { + m_errno = ERROR_BADF; + return -1; + } + Ptr dev ; + if (address.IsSingleDevice ()) + { + dev = 0; + } + else + { + m_node->GetDevice (address.GetSingleDevice ()); + } + m_node->RegisterProtocolHandler (MakeCallback (&PacketSocket::ForwardUp, this), + address.GetProtocol (), dev); + m_state = STATE_BOUND; + m_protocol = address.GetProtocol (); + m_isSingleDevice = address.IsSingleDevice (); + m_device = address.GetSingleDevice (); + return 0; +} + +int +PacketSocket::ShutdownSend (void) +{ + if (m_state == STATE_CLOSED) + { + m_errno = ERROR_BADF; + return -1; + } + m_shutdownSend = true; + return 0; +} +int +PacketSocket::ShutdownRecv (void) +{ + if (m_state == STATE_CLOSED) + { + m_errno = ERROR_BADF; + return -1; + } + m_shutdownRecv = false; + return 0; +} +int +PacketSocket::Close(void) +{ + if (m_state == STATE_CLOSED) + { + m_errno = ERROR_BADF; + return -1; + } + m_state = STATE_CLOSED; + NotifyCloseCompleted (); + return 0; +} + +int +PacketSocket::Connect(const Address &ad) +{ + PacketSocketAddress address; + if (m_state == STATE_CLOSED) + { + m_errno = ERROR_BADF; + goto error; + } + if (m_state == STATE_OPEN) + { + // connect should happen _after_ bind. + m_errno = ERROR_INVAL; // generic error condition. + goto error; + } + if (m_state == STATE_CONNECTED) + { + m_errno = ERROR_ISCONN; + goto error; + } + if (!PacketSocketAddress::IsMatchingType (ad)) + { + m_errno = ERROR_AFNOSUPPORT; + goto error; + } + m_destAddr = ad; + m_state = STATE_CONNECTED; + NotifyConnectionSucceeded (); + return 0; + error: + NotifyConnectionFailed (); + return -1; +} + +int +PacketSocket::Send (const Packet &p) +{ + if (m_state == STATE_OPEN || + m_state == STATE_BOUND) + { + m_errno = ERROR_NOTCONN; + return -1; + } + return SendTo (m_destAddr, p); +} + +int +PacketSocket::SendTo(const Address &address, const Packet &p) +{ + PacketSocketAddress ad; + if (m_state == STATE_CLOSED) + { + m_errno = ERROR_BADF; + return -1; + } + if (m_state == STATE_OPEN) + { + // XXX should return another error here. + m_errno = ERROR_INVAL; + return -1; + } + if (m_shutdownSend) + { + m_errno = ERROR_SHUTDOWN; + return -1; + } + if (!PacketSocketAddress::IsMatchingType (address)) + { + m_errno = ERROR_AFNOSUPPORT; + return -1; + } + ad = PacketSocketAddress::ConvertFrom (address); + + bool error = false; + Address dest = ad.GetPhysicalAddress (); + if (ad.IsSingleDevice ()) + { + Ptr device = m_node->GetDevice (ad.GetSingleDevice ()); + if (!device->Send (p, dest, ad.GetProtocol ())) + { + error = true; + } + } + else + { + for (uint32_t i = 0; i < m_node->GetNDevices (); i++) + { + Ptr device = m_node->GetDevice (i); + if (!device->Send (p, dest, ad.GetProtocol ())) + { + error = true; + } + } + } + if (!error) + { + NotifyDataSent (p.GetSize ()); + } + + if (error) + { + m_errno = ERROR_INVAL; + return -1; + } + else + { + return 0; + } +} + +void +PacketSocket::ForwardUp (Ptr device, const Packet &packet, + uint16_t protocol, const Address &from) +{ + if (m_shutdownRecv) + { + return; + } + + Packet p = packet; + + PacketSocketAddress address; + address.SetPhysicalAddress (from); + address.SetSingleDevice (device->GetIfIndex ()); + address.SetProtocol (protocol); + + NS_DEBUG ("PacketSocket::ForwardUp: UID is " << packet.GetUid() + << " PacketSocket " << this); + NotifyDataReceived (p, address); +} + +}//namespace ns3 diff --git a/src/node/packet-socket.h b/src/node/packet-socket.h new file mode 100644 index 000000000..8dc1b9e62 --- /dev/null +++ b/src/node/packet-socket.h @@ -0,0 +1,119 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 Emmanuelle Laprise, INRIA + * + * 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 + * + * Authors: Emmanuelle Laprise , + * Mathieu Lacage + */ +#ifndef PACKET_SOCKET_H +#define PACKET_SOCKET_H + +#include +#include "ns3/callback.h" +#include "ns3/ptr.h" +#include "ns3/socket.h" + +namespace ns3 { + +class Node; +class Packet; +class NetDevice; +class PacketSocketAddress; + +/** + * \brief A PacketSocket is a link between an application and a net device. + * + * A PacketSocket can be used to connect an application to a net + * device. The application provides the buffers of data, the socket + * conserts them to a raw packet and the net device then adds the + * protocol specific headers and trailers. This socket type + * is very similar to the linux and BSD "packet" sockets. + * + * Here is a summary of the semantics of this class: + * - Bind: Bind uses only the protocol and device fields of the + * PacketSocketAddress. If none are provided, Bind uses + * zero for both, which means that the socket is bound + * to all protocols on all devices on the node. + * + * - Connect: uses only the protocol, device and "physical address" + * field of the PacketSocketAddress. It is used to set the default + * destination address for outgoing packets. + * + * - Send: send the input packet to the underlying NetDevices + * with the default destination address. The socket must + * be bound and connected. + * + * - SendTo: uses the protocol, device, and "physical address" + * fields of the PacketSocketAddress. The device value is + * used to specialize the packet transmission to a single + * device, the protocol value specifies the protocol of this + * packet only and the "physical address" field is used to override the + * default destination address. The socket must be bound. + * + * - Recv: The address represents the address of the packer originator. + * The fields "physical address", device, and protocol are filled. + * + * - Accept: not allowed + */ +class PacketSocket : public Socket +{ +public: + PacketSocket (Ptr node); + virtual ~PacketSocket (); + + virtual enum SocketErrno GetErrno (void) const; + virtual Ptr GetNode (void) const; + virtual int Bind (void); + virtual int Bind (const Address & address); + virtual int Close (void); + virtual int ShutdownSend (void); + virtual int ShutdownRecv (void); + virtual int Connect(const Address &address); + virtual int Send (const Packet &p); + virtual int SendTo(const Address &address,const Packet &p); + + +private: + +private: + void Init (void); + void ForwardUp (Ptr device, const Packet &packet, + uint16_t protocol, const Address &from); + int DoBind (const PacketSocketAddress &address); + virtual void DoDispose (void); + + enum State { + STATE_OPEN, + STATE_BOUND, // open and bound + STATE_CONNECTED, // open, bound and connected + STATE_CLOSED + }; + Ptr m_node; + enum SocketErrno m_errno; + bool m_shutdownSend; + bool m_shutdownRecv; + enum State m_state; + uint16_t m_protocol; + bool m_isSingleDevice; + uint32_t m_device; + Address m_destAddr; /// Default destination address +}; + +}//namespace ns3 + +#endif /* PACKET_SOCKET_H */ + + diff --git a/src/node/queue.cc b/src/node/queue.cc index 69b23bc0c..72eafed69 100644 --- a/src/node/queue.cc +++ b/src/node/queue.cc @@ -31,6 +31,52 @@ const InterfaceId Queue::iid = MakeInterfaceId ("Queue", Object::iid); static ClassIdDefaultValue g_classIdDefaultValue ("Queue", "Packet Queue", Queue::iid, "DropTailQueue"); + +uint16_t +QueueTraceType::GetUid (void) +{ + static uint16_t uid = AllocateUid ("QueueTraceType"); + return uid; +} +QueueTraceType::QueueTraceType () + : m_type (QueueTraceType::ENQUEUE) +{} +QueueTraceType::QueueTraceType (enum Type type) + : m_type (type) +{} +bool +QueueTraceType::IsEnqueue (void) const +{ + return m_type == ENQUEUE; +} +bool +QueueTraceType::IsDequeue (void) const +{ + return m_type == DEQUEUE; +} +bool +QueueTraceType::IsDrop (void) const +{ + return m_type == DROP; +} + +void +QueueTraceType::Print (std::ostream &os) const +{ + os << "queue-"; + switch (m_type) { + case QueueTraceType::ENQUEUE: + os << "enqueue"; + break; + case QueueTraceType::DEQUEUE: + os << "dequeue"; + break; + case QueueTraceType::DROP: + os << "drop"; + break; + } +} + Queue::Queue() : m_nBytes(0), m_nTotalReceivedBytes(0), @@ -52,9 +98,9 @@ TraceResolver * Queue::CreateTraceResolver (TraceContext const &context) { CompositeTraceResolver *resolver = new CompositeTraceResolver (context); - resolver->Add ("enqueue", m_traceEnqueue, Queue::ENQUEUE); - resolver->Add ("dequeue", m_traceDequeue, Queue::DEQUEUE); - resolver->Add ("drop", m_traceDrop, Queue::DROP); + resolver->Add ("enqueue", m_traceEnqueue, QueueTraceType (QueueTraceType::ENQUEUE)); + resolver->Add ("dequeue", m_traceDequeue, QueueTraceType (QueueTraceType::DEQUEUE)); + resolver->Add ("drop", m_traceDrop, QueueTraceType (QueueTraceType::DROP)); return resolver; } diff --git a/src/node/queue.h b/src/node/queue.h index 02f57ca5c..d135fff68 100644 --- a/src/node/queue.h +++ b/src/node/queue.h @@ -31,11 +31,32 @@ #include "ns3/object.h" #include "ns3/callback-trace-source.h" #include "ns3/trace-resolver.h" +#include "ns3/trace-context-element.h" namespace ns3 { class StringEnumDefaultValue; +class QueueTraceType : public TraceContextElement +{ +public: + enum Type { + ENQUEUE, + DEQUEUE, + DROP + }; + static uint16_t GetUid (void); + QueueTraceType (); + QueueTraceType (enum Type type); + bool IsEnqueue (void) const; + bool IsDequeue (void) const; + bool IsDrop (void) const; + void Print (std::ostream &os) const; +private: + enum Type m_type; +}; + + /** * \brief Abstract base class for packet Queues * @@ -46,11 +67,6 @@ class Queue : public Object public: static const InterfaceId iid; - enum TraceType { - ENQUEUE, - DEQUEUE, - DROP, - }; Queue (); virtual ~Queue (); diff --git a/src/node/socket.cc b/src/node/socket.cc index 75cd3a023..58f83947f 100644 --- a/src/node/socket.cc +++ b/src/node/socket.cc @@ -1,4 +1,26 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 Georgia Tech Research Corporation + * 2007 INRIA + * + * 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 + * + * Authors: George F. Riley + * Mathieu Lacage + */ #include "socket.h" +#include "ns3/packet.h" namespace ns3 { @@ -6,76 +28,117 @@ Socket::~Socket () {} void -Socket::Close(Callback > closeCompleted) +Socket::SetCloseCallback (Callback > closeCompleted) { - DoClose (closeCompleted); + m_closeCompleted = closeCompleted; +} +void +Socket::SetConnectCallback (Callback > connectionSucceeded, + Callback > connectionFailed, + Callback > halfClose) +{ + m_connectionSucceeded = connectionSucceeded; + m_connectionFailed = connectionFailed; + m_halfClose = halfClose; +} +void +Socket::SetAcceptCallback (Callback, const Address &> connectionRequest, + Callback, const Address&> newConnectionCreated, + Callback > closeRequested) +{ + m_connectionRequest = connectionRequest; + m_newConnectionCreated = newConnectionCreated; + m_closeRequested = closeRequested; +} +void +Socket::SetSendCallback (Callback, uint32_t> dataSent) +{ + m_dataSent = dataSent; +} +void +Socket::SetRecvCallback (Callback, const Packet &,const Address&> receivedData) +{ + m_receivedData = receivedData; } void -Socket::Connect(const Ipv4Address & address, - uint16_t portNumber, - Callback > connectionSucceeded, - Callback > connectionFailed, - Callback > halfClose) +Socket::NotifyCloseCompleted (void) { - DoConnect (address, portNumber, connectionSucceeded, connectionFailed, halfClose); -} -int -Socket::Accept(Callback, const Ipv4Address&, uint16_t> connectionRequest, - Callback, const Ipv4Address&, uint16_t> newConnectionCreated, - Callback > closeRequested) -{ - return DoAccept (connectionRequest, newConnectionCreated, closeRequested); -} -int -Socket::Send (const uint8_t* buffer, - uint32_t size, - Callback, uint32_t> dataSent) -{ - return DoSend (buffer, size, dataSent); -} -int -Socket::SendTo(const Ipv4Address &address, - uint16_t port, - const uint8_t *buffer, - uint32_t size, - Callback, uint32_t> dataSent) -{ - return DoSendTo (address, port, buffer, size, dataSent); + if (!m_closeCompleted.IsNull ()) + { + m_closeCompleted (this); + } } void -Socket::Recv(Callback, const uint8_t*, uint32_t,const Ipv4Address&, uint16_t> callback) +Socket::NotifyConnectionSucceeded (void) { - DoRecv (callback); + if (!m_connectionSucceeded.IsNull ()) + { + m_connectionSucceeded (this); + } } void -Socket::RecvDummy(Callback, uint32_t,const Ipv4Address&, uint16_t> callback) +Socket::NotifyConnectionFailed (void) { - DoRecvDummy (callback); + if (!m_connectionFailed.IsNull ()) + { + m_connectionFailed (this); + } +} +void +Socket::NotifyHalfClose (void) +{ + if (!m_halfClose.IsNull ()) + { + m_halfClose (this); + } } - - bool -Socket::RefuseAllConnections (Ptr socket, const Ipv4Address& address, uint16_t port) +Socket::NotifyConnectionRequest (const Address &from) { - return false; + if (!m_connectionRequest.IsNull ()) + { + return m_connectionRequest (this, from); + } + else + { + // refuse all incomming connections by default. + return false; + } } void -Socket::DummyCallbackVoidSocket (Ptr socket) -{} -void -Socket::DummyCallbackVoidSocketUi32 (Ptr socket, uint32_t) -{} +Socket::NotifyNewConnectionCreated (Ptr socket, const Address &from) +{ + if (!m_newConnectionCreated.IsNull ()) + { + m_newConnectionCreated (socket, from); + } +} void -Socket::DummyCallbackVoidSocketUi32Ipv4AddressUi16 (Ptr socket, uint32_t, const Ipv4Address &, uint16_t) -{} +Socket::NotifyCloseRequested (void) +{ + if (!m_closeRequested.IsNull ()) + { + m_closeRequested (this); + } +} void -Socket::DummyCallbackVoidSocketBufferUi32Ipv4AddressUi16 (Ptr socket, const uint8_t *, uint32_t, - const Ipv4Address &, uint16_t) -{} +Socket::NotifyDataSent (uint32_t size) +{ + if (!m_dataSent.IsNull ()) + { + m_dataSent (this, size); + } +} void -Socket::DummyCallbackVoidSocketIpv4AddressUi16 (Ptr socket, const Ipv4Address &, uint16_t) -{} +Socket::NotifyDataReceived (const Packet &p, const Address &from) +{ + if (!m_receivedData.IsNull ()) + { + m_receivedData (this, p, from); + } +} + }//namespace ns3 diff --git a/src/node/socket.h b/src/node/socket.h index b0ec81b05..c717ec0a5 100644 --- a/src/node/socket.h +++ b/src/node/socket.h @@ -1,42 +1,44 @@ -/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ -// -// Copyright (c) 2006 Georgia Tech Research Corporation -// -// 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: George F. Riley -// +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 Georgia Tech Research Corporation + * 2007 INRIA + * + * 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 + * + * Authors: George F. Riley + * Mathieu Lacage + */ #ifndef __SOCKET_H__ #define __SOCKET_H__ #include "ns3/callback.h" #include "ns3/ptr.h" -#include "ipv4-address.h" #include "ns3/object.h" +#include "address.h" #include namespace ns3 { class Node; +class Packet; /** * \brief Define a Socket API based on the BSD Socket API. * * Contrary to the original BSD socket API, this API is asynchronous: - * it does not contain blocking calls. This API also does not use - * the dreaded BSD sockaddr_t type. Other than that, it tries to stick + * it does not contain blocking calls. Other than that, it tries to stick * to the BSD API to make it easier those who know the BSD API to use * this API. */ @@ -53,6 +55,9 @@ public: ERROR_AGAIN, ERROR_SHUTDOWN, ERROR_OPNOTSUPP, + ERROR_AFNOSUPPORT, + ERROR_INVAL, + ERROR_BADF, SOCKET_ERRNO_LAST }; @@ -68,74 +73,13 @@ public: */ virtual Ptr GetNode (void) const = 0; - /** - * Allocate a free port number and - * bind this socket to this port number on all - * interfaces of this system. - * - * \returns 0 on success, -1 on failure. - */ - virtual int Bind (void) = 0; - - /** - * Allocate a free port number and - * bind this socket to this port number on the - * specified interface. - * - * \param address address of interface to bind to. - * \returns 0 on success, -1 on failure. - */ - virtual int Bind (Ipv4Address address) = 0; - /** - * Bind this socket to this port number - * on all interfaces of this system. - * - * \param port port to bind to on all interfaces - * \returns 0 on success, -1 on failure. - */ - virtual int Bind (uint16_t port) = 0; - - /** - * Bind this socket to this port number - * on the interface specified by address. - * - * \param address address of interface to bind to. - * \param port port to bind to on specified interface - * \returns 0 on success, -1 on failure. - */ - virtual int Bind (Ipv4Address address, uint16_t port) = 0; - - /** - * \brief Close a socket. * \param closeCompleted Callback invoked when the close operation is * completed. - * - * After the Close call, the socket is no longer valid, and cannot - * safely be used for subsequent operations. */ - void Close(Callback > closeCompleted = MakeCallback (&Socket::DummyCallbackVoidSocket)); + void SetCloseCallback (Callback > closeCompleted); /** - * \returns zero on success, -1 on failure. - * - * Do not allow any further Send calls. This method is typically - * implemented for Tcp sockets by a half close. - */ - virtual int ShutdownSend (void) = 0; - - /** - * \returns zero on success, -1 on failure. - * - * Do not allow any further Recv calls. This method is typically - * implemented for Tcp sockets by a half close. - */ - virtual int ShutdownRecv (void) = 0; - - /** - * \brief Initiate a connection to a remote host - * \param address IP Address of remote. - * \param portNumber Port number of remote * \param connectionSucceeded this callback is invoked when the connection request * initiated by the user is successfully completed. The callback is passed * back a pointer to the same socket object. @@ -145,12 +89,9 @@ public: * \param halfClose XXX When exactly is this callback invoked ? If it invoked when the * other side closes the connection ? Or when I call Close ? */ - void Connect(const Ipv4Address & address, - uint16_t portNumber, - Callback > connectionSucceeded = MakeCallback(&Socket::DummyCallbackVoidSocket), - Callback > connectionFailed = MakeCallback(&Socket::DummyCallbackVoidSocket), - Callback > halfClose = MakeCallback(&Socket::DummyCallbackVoidSocket)); - + void SetConnectCallback (Callback > connectionSucceeded, + Callback > connectionFailed, + Callback > halfClose); /** * \brief Accept connection requests from remote hosts * \param connectionRequest Callback for connection request from peer. @@ -170,91 +111,99 @@ public: * \param closeRequested Callback for connection close request from peer. * XXX: when is this callback invoked ? */ - int Accept(Callback, const Ipv4Address&, uint16_t> connectionRequest = - MakeCallback(&Socket::RefuseAllConnections), - Callback, const Ipv4Address&, uint16_t> newConnectionCreated = - MakeCallback (&Socket::DummyCallbackVoidSocketIpv4AddressUi16), - Callback > closeRequested = MakeCallback (&Socket::DummyCallbackVoidSocket)); + void SetAcceptCallback (Callback, const Address &> connectionRequest, + Callback, const Address&> newConnectionCreated, + Callback > closeRequested); + void SetSendCallback (Callback, uint32_t> dataSent); + /** + * \brief Receive data + * \param receivedData Invoked whenever new data is received. + * + */ + void SetRecvCallback (Callback, const Packet &,const Address&> receivedData); + + /** + * \param address the address to try to allocate + * \returns 0 on success, -1 on failure. + * + * Allocate a local endpoint for this socket. + */ + virtual int Bind (const Address &address) = 0; + + /** + * Allocate a local endpoint for this socket. + * + * \returns 0 on success, -1 on failure. + */ + virtual int Bind () = 0; + + /** + * \brief Close a socket. + * + * After the Close call, the socket is no longer valid, and cannot + * safely be used for subsequent operations. + */ + virtual int Close(void) = 0; + /** + * \returns zero on success, -1 on failure. + * + * Do not allow any further Send calls. This method is typically + * implemented for Tcp sockets by a half close. + */ + virtual int ShutdownSend (void) = 0; + + /** + * \returns zero on success, -1 on failure. + * + * Do not allow any further Recv calls. This method is typically + * implemented for Tcp sockets by a half close. + */ + virtual int ShutdownRecv (void) = 0; + + /** + * \brief Initiate a connection to a remote host + * \param address Address of remote. + */ + virtual int Connect(const Address &address) = 0; + /** * \brief Send data (or dummy data) to the remote host - * \param buffer Data to send (nil if dummy data). - * \param size Number of bytes to send. - * \param dataSent Data sent callback. + * \param p packet to send * \returns -1 in case of error or the number of bytes copied in the * internal buffer and accepted for transmission. */ - int Send (const uint8_t* buffer, - uint32_t size, - Callback, uint32_t> dataSent = MakeCallback (&Socket::DummyCallbackVoidSocketUi32)); + virtual int Send (const Packet &p) = 0; /** * \brief Send data to a specified peer. * \param address IP Address of remote host - * \param port port number - * \param buffer Data to send (nil if dummy data). - * \param size Number of bytes to send. - * \param dataSent Data sent callback. + * \param p packet to send * \returns -1 in case of error or the number of bytes copied in the * internal buffer and accepted for transmission. */ - int SendTo(const Ipv4Address &address, - uint16_t port, - const uint8_t *buffer, - uint32_t size, - Callback, uint32_t> dataSent = MakeCallback (&Socket::DummyCallbackVoidSocketUi32)); - - /** - * \brief Receive data - * \param receivedData Invoked whenever new data is received. - * - * If you wish to transport only dummy packets, this method is not a very - * efficient way to receive these dummy packets: it will trigger a memory - * allocation to hold the dummy memory into a buffer which can be passed - * to the user. Instead, consider using the RecvDummy method. - */ - void Recv(Callback, const uint8_t*, uint32_t,const Ipv4Address&, uint16_t> receivedData = - MakeCallback (&Socket::DummyCallbackVoidSocketBufferUi32Ipv4AddressUi16)); - - /** - * \brief Receive data - * \param receivedData Invoked whenever new data is received. - * - * This method is included because it is vastly more efficient than the - * Recv method when you use dummy payload. - */ - void RecvDummy(Callback, uint32_t,const Ipv4Address&, uint16_t> receivedData = - MakeCallback (&Socket::DummyCallbackVoidSocketUi32Ipv4AddressUi16)); + virtual int SendTo(const Address &address,const Packet &p) = 0; -private: - virtual void DoClose(Callback > closeCompleted) = 0; - virtual void DoConnect(const Ipv4Address & address, - uint16_t portNumber, - Callback > connectionSucceeded, - Callback > connectionFailed, - Callback > halfClose) = 0; - virtual int DoAccept(Callback, const Ipv4Address&, uint16_t> connectionRequest, - Callback, const Ipv4Address&, uint16_t> newConnectionCreated, - Callback > closeRequested) = 0; - virtual int DoSend (const uint8_t* buffer, - uint32_t size, - Callback, uint32_t> dataSent) = 0; - virtual int DoSendTo(const Ipv4Address &address, - uint16_t port, - const uint8_t *buffer, - uint32_t size, - Callback, uint32_t> dataSent) = 0; - virtual void DoRecv(Callback, const uint8_t*, uint32_t,const Ipv4Address&, uint16_t> receive) = 0; - virtual void DoRecvDummy(Callback, uint32_t,const Ipv4Address&, uint16_t>) = 0; +protected: + void NotifyCloseCompleted (void); + void NotifyConnectionSucceeded (void); + void NotifyConnectionFailed (void); + void NotifyHalfClose (void); + bool NotifyConnectionRequest (const Address &from); + void NotifyNewConnectionCreated (Ptr socket, const Address &from); + void NotifyCloseRequested (void); + void NotifyDataSent (uint32_t size); + void NotifyDataReceived (const Packet &p, const Address &from); - - static bool RefuseAllConnections (Ptr socket, const Ipv4Address& address, uint16_t port); - static void DummyCallbackVoidSocket (Ptr socket); - static void DummyCallbackVoidSocketUi32 (Ptr socket, uint32_t); - static void DummyCallbackVoidSocketUi32Ipv4AddressUi16 (Ptr socket, uint32_t, const Ipv4Address &, uint16_t); - static void DummyCallbackVoidSocketBufferUi32Ipv4AddressUi16 (Ptr socket, const uint8_t *, uint32_t, - const Ipv4Address &, uint16_t); - static void DummyCallbackVoidSocketIpv4AddressUi16 (Ptr socket, const Ipv4Address &, uint16_t); + Callback > m_closeCompleted; + Callback > m_connectionSucceeded; + Callback > m_connectionFailed; + Callback > m_halfClose; + Callback > m_closeRequested; + Callback, const Address &> m_connectionRequest; + Callback, const Address&> m_newConnectionCreated; + Callback, uint32_t> m_dataSent; + Callback, const Packet &,const Address&> m_receivedData; }; } //namespace ns3 diff --git a/src/node/wscript b/src/node/wscript index 4b1ea1f01..b9fd9552a 100644 --- a/src/node/wscript +++ b/src/node/wscript @@ -1,16 +1,17 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- def build(bld): - node = bld.create_obj('cpp', 'shlib') - node.name = 'ns3-node' - node.target = node.name - node.uselib_local = ['ns3-core', 'ns3-common', 'ns3-simulator'] + node = bld.create_ns3_module('node', ['core', 'common', 'simulator']) node.source = [ + 'address.cc', + 'eui48-address.cc', + 'eui64-address.cc', + 'inet-socket-address.cc', + 'packet-socket-address.cc', 'node.cc', 'ipv4-address.cc', 'net-device.cc', - 'mac-address.cc', - 'address-utils.cc', + 'address-utils.cc', 'llc-snap-header.cc', 'ethernet-header.cc', 'ethernet-trailer.cc', @@ -21,6 +22,8 @@ def build(bld): 'node-list.cc', 'socket.cc', 'socket-factory.cc', + 'packet-socket-factory.cc', + 'packet-socket.cc', 'udp.cc', 'ipv4.cc', 'application.cc', @@ -28,11 +31,15 @@ def build(bld): headers = bld.create_obj('ns3header') headers.source = [ + 'address.h', + 'eui48-address.h', + 'eui64-address.h', + 'inet-socket-address.h', + 'packet-socket-address.h', 'node.h', 'ipv4-address.h', 'net-device.h', - 'mac-address.h', - 'address-utils.h', + 'address-utils.h', 'ipv4-route.h', 'queue.h', 'drop-tail-queue.h', @@ -43,6 +50,7 @@ def build(bld): 'node-list.h', 'socket.h', 'socket-factory.h', + 'packet-socket-factory.h', 'udp.h', 'ipv4.h', 'application.h', diff --git a/src/routing/global-routing/candidate-queue.cc b/src/routing/global-routing/candidate-queue.cc new file mode 100644 index 000000000..67bec63b1 --- /dev/null +++ b/src/routing/global-routing/candidate-queue.cc @@ -0,0 +1,150 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#include "ns3/debug.h" +#include "ns3/assert.h" +#include "candidate-queue.h" + +NS_DEBUG_COMPONENT_DEFINE ("CandidateQueue"); + +namespace ns3 { + +CandidateQueue::CandidateQueue() + : m_candidates () +{ + NS_DEBUG("CandidateQueue::CandidateQueue ()"); +} + +CandidateQueue::~CandidateQueue() +{ + NS_DEBUG("CandidateQueue::~CandidateQueue ()"); + Clear (); +} + + void +CandidateQueue::Clear (void) +{ + NS_DEBUG("CandidateQueue::Clear ()"); + + while (!m_candidates.empty ()) + { + SPFVertex *p = Pop (); + delete p; + p = 0; + } +} + + void +CandidateQueue::Push (SPFVertex *vNew) +{ + NS_DEBUG("CandidateQueue::Push (" << vNew << ")"); + + CandidateList_t::iterator i = m_candidates.begin (); + + for (; i != m_candidates.end (); i++) + { + SPFVertex *v = *i; + if (vNew->GetDistanceFromRoot () < v->GetDistanceFromRoot ()) + { + break; + } + } + m_candidates.insert(i, vNew); +} + + SPFVertex * +CandidateQueue::Pop (void) +{ + NS_DEBUG("CandidateQueue::Pop ()"); + + if (m_candidates.empty ()) + { + return 0; + } + + SPFVertex *v = m_candidates.front (); + m_candidates.pop_front (); + return v; +} + + SPFVertex * +CandidateQueue::Top (void) const +{ + NS_DEBUG("CandidateQueue::Top ()"); + + if (m_candidates.empty ()) + { + return 0; + } + + return m_candidates.front (); +} + + bool +CandidateQueue::Empty (void) const +{ + NS_DEBUG("CandidateQueue::Empty ()"); + + return m_candidates.empty (); +} + + uint32_t +CandidateQueue::Size (void) const +{ + NS_DEBUG("CandidateQueue::Size ()"); + + return m_candidates.size (); +} + + SPFVertex * +CandidateQueue::Find (const Ipv4Address addr) const +{ + NS_DEBUG("CandidateQueue::Find ()"); + + CandidateList_t::const_iterator i = m_candidates.begin (); + + for (; i != m_candidates.end (); i++) + { + SPFVertex *v = *i; + if (v->GetVertexId() == addr) + { + return v; + } + } + + return 0; +} + + void +CandidateQueue::Reorder (void) +{ + NS_DEBUG("CandidateQueue::Reorder ()"); + + std::list temp; + + while (!m_candidates.empty ()) { + SPFVertex *v = m_candidates.front (); + m_candidates.pop_front (); + temp.push_back(v); + } + + while (!temp.empty ()) { + Push (temp.front ()); + temp.pop_front (); + } +} + +} // namespace ns3 diff --git a/src/routing/global-routing/candidate-queue.h b/src/routing/global-routing/candidate-queue.h new file mode 100644 index 000000000..09b3a5c22 --- /dev/null +++ b/src/routing/global-routing/candidate-queue.h @@ -0,0 +1,181 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#ifndef CANDIDATE_QUEUE_H +#define CANDIDATE_QUEUE_H + +#include +#include +#include "global-route-manager-impl.h" + +namespace ns3 { + +/** + * \brief A Candidate Queue used in static routing. + * + * The CandidateQueue is used in the OSPF shortest path computations. It + * is a priority queue used to store candidates for the shortest path to a + * given network. + * + * The queue holds Shortest Path First Vertex pointers and orders them + * according to the lowest value of the field m_distanceFromRoot. Remaining + * vertices are ordered according to increasing distance. This implements a + * priority queue. + * + * Although a STL priority_queue almost does what we want, the requirement + * for a Find () operation, the dynamic nature of the data and the derived + * requirement for a Reorder () operation led us to implement this simple + * enhanced priority queue. + */ +class CandidateQueue +{ +public: +/** + * @brief Create an empty SPF Candidate Queue. + * @internal + * + * @see SPFVertex + */ + CandidateQueue (); + +/** + * @internal Destroy an SPF Candidate Queue and release any resources held + * by the contents. + * @internal + * + * @see SPFVertex + */ + virtual ~CandidateQueue (); + +/** + * @brief Empty the Candidate Queue and release all of the resources + * associated with the Shortest Path First Vertex pointers in the queue. + * @internal + * + * @see SPFVertex + */ + void Clear (void); + +/** + * @brief Push a Shortest Path First Vertex pointer onto the queue according + * to the priority scheme. + * @internal + * + * On completion, the top of the queue will hold the Shortest Path First + * Vertex pointer that points to a vertex having lowest value of the field + * m_distanceFromRoot. Remaining vertices are ordered according to + * increasing distance. + * + * @see SPFVertex + * @param vNew The Shortest Path First Vertex to add to the queue. + */ + void Push (SPFVertex *vNew); + +/** + * @brief Pop the Shortest Path First Vertex pointer at the top of the queue. + * @internal + * + * The caller is given the responsiblity for releasing the resources + * associated with the vertex. + * + * @see SPFVertex + * @see Top () + * @returns The Shortest Path First Vertex pointer at the top of the queue. + */ + SPFVertex* Pop (void); + +/** + * @brief Return the Shortest Path First Vertex pointer at the top of the + * queue. + * @internal + * + * This method does not pop the SPFVertex* off of the queue, it simply + * returns the pointer. + * + * @see SPFVertex + * @see Pop () + * @returns The Shortest Path First Vertex pointer at the top of the queue. + */ + SPFVertex* Top (void) const; + +/** + * @brief Test the Candidate Queue to determine if it is empty. + * @internal + * + * @returns True if the queue is empty, false otherwise. + */ + bool Empty (void) const; + +/** + * @brief Return the number of Shortest Path First Vertex pointers presently + * stored in the Candidate Queue. + * @internal + * + * @see SPFVertex + * @returns The number of SPFVertex* pointers in the Candidate Queue. + */ + uint32_t Size (void) const; + +/** + * @brief Searches the Candidate Queue for a Shortest Path First Vertex + * pointer that points to a vertex having the given IP address. + * @internal + * + * @see SPFVertex + * @param addr The IP address to search for. + * @returns The SPFVertex* pointer corresponding to the given IP address. + */ + SPFVertex* Find (const Ipv4Address addr) const; + +/** + * @brief Reorders the Candidate Queue according to the priority scheme. + * @internal + * + * On completion, the top of the queue will hold the Shortest Path First + * Vertex pointer that points to a vertex having lowest value of the field + * m_distanceFromRoot. Remaining vertices are ordered according to + * increasing distance. + * + * This method is provided in case the values of m_distanceFromRoot change + * during the routing calculations. + * + * @see SPFVertex + */ + void Reorder (void); + +protected: + typedef std::list CandidateList_t; + CandidateList_t m_candidates; + +private: +/** + * Candidate Queue copy construction is disallowed (not implemented) to + * prevent the compiler from slipping in incorrect versions that don't + * properly deal with deep copies. + */ + CandidateQueue (CandidateQueue& sr); + +/** + * Candidate Queue assignment operator is disallowed (not implemented) to + * prevent the compiler from slipping in incorrect versions that don't + * properly deal with deep copies. + */ + CandidateQueue& operator= (CandidateQueue& sr); +}; + +} // namespace ns3 + +#endif /* CANDIDATE_QUEUE_H */ diff --git a/src/routing/global-routing/global-route-manager-impl.cc b/src/routing/global-routing/global-route-manager-impl.cc new file mode 100644 index 000000000..42c63b9b9 --- /dev/null +++ b/src/routing/global-routing/global-route-manager-impl.cc @@ -0,0 +1,1711 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#include +#include +#include +#include "ns3/assert.h" +#include "ns3/fatal-error.h" +#include "ns3/debug.h" +#include "ns3/node-list.h" +#include "ns3/ipv4.h" +#include "global-router-interface.h" +#include "global-route-manager-impl.h" +#include "candidate-queue.h" + +NS_DEBUG_COMPONENT_DEFINE ("GlobalRouteManager"); + +namespace ns3 { + +// --------------------------------------------------------------------------- +// +// SPFVertex Implementation +// +// --------------------------------------------------------------------------- + +SPFVertex::SPFVertex () : + m_vertexType (VertexUnknown), + m_vertexId ("255.255.255.255"), + m_lsa (0), + m_distanceFromRoot (SPF_INFINITY), + m_rootOif (SPF_INFINITY), + m_nextHop ("0.0.0.0"), + m_parent (0), + m_children () +{ +} + +SPFVertex::SPFVertex (GlobalRoutingLSA* lsa) : + m_vertexId (lsa->GetLinkStateId ()), + m_lsa (lsa), + m_distanceFromRoot (SPF_INFINITY), + m_rootOif (SPF_INFINITY), + m_nextHop ("0.0.0.0"), + m_parent (0), + m_children () +{ + if (lsa->GetLSType () == GlobalRoutingLSA::RouterLSA) + { + NS_DEBUG ("SPFVertex:: setting m_vertexType to VertexRouter"); + m_vertexType = SPFVertex::VertexRouter; + } + else if (lsa->GetLSType () == GlobalRoutingLSA::NetworkLSA) + { + NS_DEBUG ("SPFVertex:: setting m_vertexType to VertexNetwork"); + m_vertexType = SPFVertex::VertexNetwork; + } +} + +SPFVertex::~SPFVertex () +{ + for ( ListOfSPFVertex_t::iterator i = m_children.begin (); + i != m_children.end (); + i++) + { + SPFVertex *p = *i; + delete p; + p = 0; + *i = 0; + } + m_children.clear (); +} + + void +SPFVertex::SetVertexType (SPFVertex::VertexType type) +{ + m_vertexType = type; +} + + SPFVertex::VertexType +SPFVertex::GetVertexType (void) const +{ + return m_vertexType; +} + + void +SPFVertex::SetVertexId (Ipv4Address id) +{ + m_vertexId = id; +} + + Ipv4Address +SPFVertex::GetVertexId (void) const +{ + return m_vertexId; +} + + void +SPFVertex::SetLSA (GlobalRoutingLSA* lsa) +{ + m_lsa = lsa; +} + + GlobalRoutingLSA* +SPFVertex::GetLSA (void) const +{ + return m_lsa; +} + + void +SPFVertex::SetDistanceFromRoot (uint32_t distance) +{ + m_distanceFromRoot = distance; +} + + uint32_t +SPFVertex::GetDistanceFromRoot (void) const +{ + return m_distanceFromRoot; +} + + void +SPFVertex::SetOutgoingInterfaceId (uint32_t id) +{ + m_rootOif = id; +} + + uint32_t +SPFVertex::GetOutgoingInterfaceId (void) const +{ + return m_rootOif; +} + + void +SPFVertex::SetNextHop (Ipv4Address nextHop) +{ + m_nextHop = nextHop; +} + + Ipv4Address +SPFVertex::GetNextHop (void) const +{ + return m_nextHop; +} + + void +SPFVertex::SetParent (SPFVertex* parent) +{ + m_parent = parent; +} + + SPFVertex* +SPFVertex::GetParent (void) const +{ + return m_parent; +} + + uint32_t +SPFVertex::GetNChildren (void) const +{ + return m_children.size (); +} + + SPFVertex* +SPFVertex::GetChild (uint32_t n) const +{ + uint32_t j = 0; + + for ( ListOfSPFVertex_t::const_iterator i = m_children.begin (); + i != m_children.end (); + i++, j++) + { + if (j == n) + { + return *i; + } + } + NS_ASSERT_MSG(false, "Index out of range."); + return 0; +} + + uint32_t +SPFVertex::AddChild (SPFVertex* child) +{ + m_children.push_back (child); + return m_children.size (); +} + +// --------------------------------------------------------------------------- +// +// GlobalRouteManagerLSDB Implementation +// +// --------------------------------------------------------------------------- + +GlobalRouteManagerLSDB::GlobalRouteManagerLSDB () +: + m_database () +{ + NS_DEBUG ("GlobalRouteManagerLSDB::GlobalRouteManagerLSDB ()"); +} + +GlobalRouteManagerLSDB::~GlobalRouteManagerLSDB () +{ + NS_DEBUG ("GlobalRouteManagerLSDB::~GlobalRouteManagerLSDB ()"); + + LSDBMap_t::iterator i; + for (i= m_database.begin (); i!= m_database.end (); i++) + { + NS_DEBUG ("GlobalRouteManagerLSDB::~GlobalRouteManagerLSDB ():free LSA"); + GlobalRoutingLSA* temp = i->second; + delete temp; + } + NS_DEBUG ("GlobalRouteManagerLSDB::~GlobalRouteManagerLSDB (): clear map"); + m_database.clear (); +} + + void +GlobalRouteManagerLSDB::Initialize () +{ + NS_DEBUG ("GlobalRouteManagerLSDB::Initialize ()"); + + LSDBMap_t::iterator i; + for (i= m_database.begin (); i!= m_database.end (); i++) + { + GlobalRoutingLSA* temp = i->second; + temp->SetStatus (GlobalRoutingLSA::LSA_SPF_NOT_EXPLORED); + } +} + + void +GlobalRouteManagerLSDB::Insert (Ipv4Address addr, GlobalRoutingLSA* lsa) +{ + NS_DEBUG ("GlobalRouteManagerLSDB::Insert ()"); + m_database.insert (LSDBPair_t (addr, lsa)); +} + + GlobalRoutingLSA* +GlobalRouteManagerLSDB::GetLSA (Ipv4Address addr) const +{ + NS_DEBUG ("GlobalRouteManagerLSDB::GetLSA ()"); +// +// Look up an LSA by its address. +// + LSDBMap_t::const_iterator i; + for (i= m_database.begin (); i!= m_database.end (); i++) + { + if (i->first == addr) + { + return i->second; + } + } + return 0; +} + + GlobalRoutingLSA* +GlobalRouteManagerLSDB::GetLSAByLinkData (Ipv4Address addr) const +{ + NS_DEBUG ("GlobalRouteManagerLSDB::GetLSAByLinkData ()"); +// +// Look up an LSA by its address. +// + LSDBMap_t::const_iterator i; + for (i= m_database.begin (); i!= m_database.end (); i++) + { + GlobalRoutingLSA* temp = i->second; +// Iterate among temp's Link Records + for (uint32_t j = 0; j < temp->GetNLinkRecords (); j++) + { + GlobalRoutingLinkRecord *lr = temp->GetLinkRecord (j); + if ( lr->GetLinkType () == GlobalRoutingLinkRecord::TransitNetwork && + lr->GetLinkData () == addr) + { + return temp; + } + } + } + return 0; +} + +// --------------------------------------------------------------------------- +// +// GlobalRouteManagerImpl Implementation +// +// --------------------------------------------------------------------------- + +GlobalRouteManagerImpl::GlobalRouteManagerImpl () +: + m_spfroot (0) +{ + NS_DEBUG ("GlobalRouteManagerImpl::GlobalRoutemanagerImpl ()"); + m_lsdb = new GlobalRouteManagerLSDB (); +} + +GlobalRouteManagerImpl::~GlobalRouteManagerImpl () +{ + NS_DEBUG ("GlobalRouteManagerImpl::~GlobalRouteManagerImpl ()"); + + if (m_lsdb) + { + delete m_lsdb; + } +} + + void +GlobalRouteManagerImpl::DebugUseLsdb (GlobalRouteManagerLSDB* lsdb) +{ + NS_DEBUG ("GlobalRouteManagerImpl::DebugUseLsdb ()"); + + if (m_lsdb) + { + delete m_lsdb; + } + m_lsdb = lsdb; +} + +// +// In order to build the routing database, we need at least one of the nodes +// to participate as a router. Eventually we expect to provide a mechanism +// for selecting a subset of the nodes to participate; for now, we just make +// all nodes routers. We do this by walking the list of nodes in the system +// and aggregating a Global Router Interface to each of the nodes. +// + void +GlobalRouteManagerImpl::SelectRouterNodes () +{ + NS_DEBUG ("GlobalRouteManagerImpl::SelectRouterNodes ()"); + + for (NodeList::Iterator i = NodeList::Begin (); i != NodeList::End (); i++) + { + Ptr node = *i; + NS_DEBUG ("GlobalRouteManagerImpl::SelectRouterNodes (): " + "Adding GlobalRouter interface to node " << + node->GetId ()); + + Ptr globalRouter = Create (node); + node->AddInterface (globalRouter); + } +} + +// +// In order to build the routing database, we need to walk the list of nodes +// in the system and look for those that support the GlobalRouter interface. +// These routers will export a number of Link State Advertisements (LSAs) +// that describe the links and networks that are "adjacent" (i.e., that are +// on the other side of a point-to-point link). We take these LSAs and put +// add them to the Link State DataBase (LSDB) from which the routes will +// ultimately be computed. +// + void +GlobalRouteManagerImpl::BuildGlobalRoutingDatabase () +{ + NS_DEBUG ("GlobalRouteManagerImpl::BuildGlobalRoutingDatabase()"); +// +// Walk the list of nodes looking for the GlobalRouter Interface. +// + for (NodeList::Iterator i = NodeList::Begin (); i != NodeList::End (); i++) + { + Ptr node = *i; + + Ptr rtr = + node->QueryInterface (GlobalRouter::iid); +// +// Ignore nodes that aren't participating in routing. +// + if (!rtr) + { + continue; + } +// +// You must call DiscoverLSAs () before trying to use any routing info or to +// update LSAs. DiscoverLSAs () drives the process of discovering routes in +// the GlobalRouter. Afterward, you may use GetNumLSAs (), which is a very +// computationally inexpensive call. If you call GetNumLSAs () before calling +// DiscoverLSAs () will get zero as the number since no routes have been +// found. +// + uint32_t numLSAs = rtr->DiscoverLSAs (); + NS_DEBUG ("Discover LSAs: Found " << numLSAs << " LSAs"); + + for (uint32_t j = 0; j < numLSAs; ++j) + { + GlobalRoutingLSA* lsa = new GlobalRoutingLSA (); +// +// This is the call to actually fetch a Link State Advertisement from the +// router. +// + rtr->GetLSA (j, *lsa); + NS_DEBUG (*lsa); +// +// Write the newly discovered link state advertisement to the database. +// + m_lsdb->Insert (lsa->GetLinkStateId (), lsa); + } + } +} + +// +// For each node that is a global router (which is determined by the presence +// of an aggregated GlobalRouter interface), run the Dijkstra SPF calculation +// on the database rooted at that router, and populate the node forwarding +// tables. +// +// This function parallels RFC2328, Section 16.1.1, and quagga ospfd +// +// This calculation yields the set of intra-area routes associated +// with an area (called hereafter Area A). A router calculates the +// shortest-path tree using itself as the root. The formation +// of the shortest path tree is done here in two stages. In the +// first stage, only links between routers and transit networks are +// considered. Using the Dijkstra algorithm, a tree is formed from +// this subset of the link state database. In the second stage, +// leaves are added to the tree by considering the links to stub +// networks. +// +// The area's link state database is represented as a directed graph. +// The graph's vertices are routers, transit networks and stub networks. +// +// The first stage of the procedure (i.e., the Dijkstra algorithm) +// can now be summarized as follows. At each iteration of the +// algorithm, there is a list of candidate vertices. Paths from +// the root to these vertices have been found, but not necessarily +// the shortest ones. However, the paths to the candidate vertex +// that is closest to the root are guaranteed to be shortest; this +// vertex is added to the shortest-path tree, removed from the +// candidate list, and its adjacent vertices are examined for +// possible addition to/modification of the candidate list. The +// algorithm then iterates again. It terminates when the candidate +// list becomes empty. +// + void +GlobalRouteManagerImpl::InitializeRoutes () +{ + NS_DEBUG ("GlobalRouteManagerImpl::InitializeRoutes ()"); +// +// Walk the list of nodes in the system. +// + for (NodeList::Iterator i = NodeList::Begin (); i != NodeList::End (); i++) + { + Ptr node = *i; +// +// Look for the GlobalRouter interface that indicates that the node is +// participating in routing. +// + Ptr rtr = + node->QueryInterface (GlobalRouter::iid); +// +// if the node has a global router interface, then run the global routing +// algorithms. +// + if (rtr && rtr->GetNumLSAs () ) + { + SPFCalculate (rtr->GetRouterId ()); + } + } +} + +// +// This method is derived from quagga ospf_spf_next (). See RFC2328 Section +// 16.1 (2) for further details. +// +// We're passed a parameter that is a vertex which is already in the SPF +// tree. A vertex represents a router node. We also get a reference to the +// SPF candidate queue, which is a priority queue containing the shortest paths +// to the networks we know about. +// +// We examine the links in v's LSA and update the list of candidates with any +// vertices not already on the list. If a lower-cost path is found to a +// vertex already on the candidate list, store the new (lower) cost. +// + void +GlobalRouteManagerImpl::SPFNext (SPFVertex* v, CandidateQueue& candidate) +{ + SPFVertex* w = 0; + GlobalRoutingLSA* w_lsa = 0; + GlobalRoutingLinkRecord *l = 0; + uint32_t distance = 0; + uint32_t numRecordsInVertex = 0; + + NS_DEBUG ("GlobalRouteManagerImpl::SPFNext ()"); + +// V points to a Router-LSA or Network-LSA +// Loop over the links in router LSA or attached routers in Network LSA + if (v->GetVertexType () == SPFVertex::VertexRouter) + { + numRecordsInVertex = v->GetLSA ()->GetNLinkRecords (); + } + if (v->GetVertexType () == SPFVertex::VertexNetwork) + { + numRecordsInVertex = v->GetLSA ()->GetNAttachedRouters (); + } + + for (uint32_t i = 0; i < numRecordsInVertex; i++) + { +// Get w_lsa: In case of V is Router-LSA + if (v->GetVertexType () == SPFVertex::VertexRouter) + { + NS_DEBUG ("SPFNext: Examining " << v->GetVertexId () << "'s " << + v->GetLSA ()->GetNLinkRecords () << " link records"); +// +// (a) If this is a link to a stub network, examine the next link in V's LSA. +// Links to stub networks will be considered in the second stage of the +// shortest path calculation. +// + l = v->GetLSA ()->GetLinkRecord (i); + if (l->GetLinkType () == GlobalRoutingLinkRecord::StubNetwork) + { + NS_DEBUG ("SPFNext: Found a Stub record to " << + l->GetLinkId ()); + continue; + } +// +// (b) Otherwise, W is a transit vertex (router or transit network). Look up +// the vertex W's LSA (router-LSA or network-LSA) in Area A's link state +// database. +// + if (l->GetLinkType () == GlobalRoutingLinkRecord::PointToPoint) + { +// +// Lookup the link state advertisement of the new link -- we call it in +// the link state database. +// + w_lsa = m_lsdb->GetLSA (l->GetLinkId ()); + NS_ASSERT (w_lsa); + NS_DEBUG ("SPFNext: Found a P2P record from " << + v->GetVertexId () << " to " << w_lsa->GetLinkStateId ()); + } + else if (l->GetLinkType () == + GlobalRoutingLinkRecord::TransitNetwork) + { + w_lsa = m_lsdb->GetLSA (l->GetLinkId ()); + NS_ASSERT (w_lsa); + NS_DEBUG ("SPFNext: Found a Transit record from " << + v->GetVertexId () << " to " << w_lsa->GetLinkStateId ()); + } + else + { + NS_ASSERT_MSG (0, "illegal Link Type"); + } + } +// Get w_lsa: In case of V is Network-LSA + if (v->GetVertexType () == SPFVertex::VertexNetwork) + { + w_lsa = m_lsdb->GetLSAByLinkData + (v->GetLSA ()->GetAttachedRouter (i)); + if (!w_lsa) + { + continue; + } + NS_DEBUG ("SPFNext: Found a Network LSA from " << + v->GetVertexId () << " to " << w_lsa->GetLinkStateId ()); + } + +// Note: w_lsa at this point may be either RouterLSA or NetworkLSA +// +// (c) If vertex W is already on the shortest-path tree, examine the next +// link in the LSA. +// +// If the link is to a router that is already in the shortest path first tree +// then we have it covered -- ignore it. +// + if (w_lsa->GetStatus () == GlobalRoutingLSA::LSA_SPF_IN_SPFTREE) + { + NS_DEBUG ("SPFNext: Skipping-> LSA "<< + w_lsa->GetLinkStateId () << " already in SPF tree"); + continue; + } +// +// (d) Calculate the link state cost D of the resulting path from the root to +// vertex W. D is equal to the sum of the link state cost of the (already +// calculated) shortest path to vertex V and the advertised cost of the link +// between vertices V and W. +// + if (v->GetLSA ()->GetLSType () == GlobalRoutingLSA::RouterLSA) + { + distance = v->GetDistanceFromRoot () + l->GetMetric (); + } + else + { + distance = v->GetDistanceFromRoot (); + } + + NS_DEBUG ("SPFNext: Considering w_lsa " << w_lsa->GetLinkStateId ()); + +// Is there already vertex w in candidate list? + if (w_lsa->GetStatus () == GlobalRoutingLSA::LSA_SPF_NOT_EXPLORED) + { + +// prepare vertex w + w = new SPFVertex (w_lsa); +// Calculate nexthop to w +// We need to figure out how to actually get to the new router represented +// by . This will (among other things) find the next hop address to send +// packets destined for this network to, and also find the outbound interface +// used to forward the packets. +// + if (SPFNexthopCalculation (v, w, l, distance)) + { + w_lsa->SetStatus (GlobalRoutingLSA::LSA_SPF_CANDIDATE); +// +// Push this new vertex onto the priority queue (ordered by distance from the +// root node). +// + candidate.Push (w); + NS_DEBUG ("SPFNext: Pushing " << + w->GetVertexId () << ", parent vertexId: " << + v->GetVertexId ()); + } + } + else if (w_lsa->GetStatus () == GlobalRoutingLSA::LSA_SPF_CANDIDATE) + { +// +// We have already considered the link represented by . What wse have to +// do now is to decide if this new router represents a route with a shorter +// distance metric. +// +// So, locate the vertex in the candidate queue and take a look at the +// distance. + w = candidate.Find (w_lsa->GetLinkStateId ()); + if (w->GetDistanceFromRoot () < distance) + { +// +// This is not a shorter path, so don't do anything. +// + continue; + } + else if (w->GetDistanceFromRoot () == distance) + { +// +// This path is one with an equal cost. Do nothing for now -- we're not doing +// equal-cost multipath cases yet. +// + } + else + { +// +// this path represents a new, lower-cost path to (the vertex we found in +// the current link record of the link state advertisement of the current root +// (vertex ) +// +// N.B. the nexthop_calculation is conditional, if it finds a valid nexthop +// it will call spf_add_parents, which will flush the old parents +// + if (SPFNexthopCalculation (v, w, l, distance)) + { +// +// If we've changed the cost to get to the vertex represented by , we +// must reorder the priority queue keyed to that cost. +// + candidate.Reorder (); + } + } // new lower cost path found + } // end W is already on the candidate list + } // end loop over the links in V's LSA +} + +// +// This method is derived from quagga ospf_next_hop_calculation() 16.1.1. +// +// Calculate nexthop from root through V (parent) to vertex W (destination) +// with given distance from root->W. +// +// As appropriate, set w's parent, distance, and nexthop information +// +// For now, this is greatly simplified from the quagga code +// + int +GlobalRouteManagerImpl::SPFNexthopCalculation ( + SPFVertex* v, + SPFVertex* w, + GlobalRoutingLinkRecord* l, + uint32_t distance) +{ + NS_DEBUG ("GlobalRouteManagerImpl::SPFNexthopCalculation ()"); + +// If w is a NetworkVertex, l should be null +/* + if (w->GetVertexType () == SPFVertex::VertexNetwork && l) + { + NS_ASSERT_MSG(0, "Error: SPFNexthopCalculation parameter problem"); + } +*/ + +// +// The vertex m_spfroot is a distinguished vertex representing the node at +// the root of the calculations. That is, it is the node for which we are +// calculating the routes. +// +// There are two distinct cases for calculating the next hop information. +// First, if we're considering a hop from the root to an "adjacent" network +// (one that is on the other side of a point-to-point link connected to the +// root), then we need to store the information needed to forward down that +// link. The second case is if the network is not directly adjacent. In that +// case we need to use the forwarding information from the vertex on the path +// to the destination that is directly adjacent [node 1] in both cases of the +// diagram below. +// +// (1) [root] -> [point-to-point] -> [node 1] +// (2) [root] -> [point-to-point] -> [node 1] -> [point-to-point] -> [node 2] +// +// We call the propagation of next hop information down vertices of a path +// "inheriting" the next hop information. +// +// The point-to-point link information is only useful in this calculation when +// we are examining the root node. +// + if (v == m_spfroot) + { +// +// In this case is the root node, which means it is the starting point +// for the packets forwarded by that node. This also means that the next hop +// address of packets headed for some arbitrary off-network destination must +// be the destination at the other end of one of the links off of the root +// node if this root node is a router. We then need to see if this node +// is a router. +// + if (w->GetVertexType () == SPFVertex::VertexRouter) + { +// +// In the case of point-to-point links, the link data field (m_linkData) of a +// Global Router Link Record contains the local IP address. If we look at the +// link record describing the link from the perspecive of (the remote +// node from the viewpoint of ) back to the root node, we can discover the +// IP address of the router to which is adjacent. This is a distinguished +// address -- the next hop address to get from to and all networks +// accessed through that path. +// +// SPFGetNextLink () is a little odd. used in this way it is just going to +// return the link record describing the link from to . Think of it as +// SPFGetLink. +// + NS_ASSERT(l); + GlobalRoutingLinkRecord *linkRemote = 0; + linkRemote = SPFGetNextLink (w, v, linkRemote); +// +// At this point, is the Global Router Link Record describing the point- +// to point link from to from the perspective of ; and +// is the Global Router Link Record describing that same link from the +// perspective of (back to ). Now we can just copy the next hop +// address from the m_linkData member variable. +// +// The next hop member variable we put in has the sense "in order to get +// from the root node to the host represented by vertex , you have to send +// the packet to the next hop address specified in w->m_nextHop. +// + w->SetNextHop(linkRemote->GetLinkData ()); +// +// Now find the outgoing interface corresponding to the point to point link +// from the perspective of -- remember that is the link "from" +// "to" . +// + w->SetOutgoingInterfaceId ( + FindOutgoingInterfaceId (l->GetLinkData ())); + + NS_DEBUG ("SPFNexthopCalculation: Next hop from " << + v->GetVertexId () << " to " << w->GetVertexId () << + " goes through next hop " << w->GetNextHop () << + " via outgoing interface " << w->GetOutgoingInterfaceId ()); + } // end W is a router vertes + else + { + NS_ASSERT (w->GetVertexType () == SPFVertex::VertexNetwork); +// W is a directly connected network; no next hop is required + GlobalRoutingLSA* w_lsa = w->GetLSA (); + NS_ASSERT (w_lsa->GetLSType () == GlobalRoutingLSA::NetworkLSA); +// Find outgoing interface ID for this network + w->SetOutgoingInterfaceId ( + FindOutgoingInterfaceId (w_lsa->GetLinkStateId (), + w_lsa->GetNetworkLSANetworkMask () )); + w->SetDistanceFromRoot (distance); + w->SetParent (v); + NS_DEBUG ("SPFNexthopCalculation: Next hop from " << + v->GetVertexId () << " to network " << w->GetVertexId () << + " via outgoing interface " << w->GetOutgoingInterfaceId ()); + return 1; + } + } // end v is the root + else if (v->GetVertexType () == SPFVertex::VertexNetwork) + { +// See if any of v's parents are the root + if (v->GetParent () == m_spfroot) + { +// 16.1.1 para 5. ...the parent vertex is a network that +// directly connects the calculating router to the destination +// router. The list of next hops is then determined by +// examining the destination's router-LSA... + NS_ASSERT (w->GetVertexType () == SPFVertex::VertexRouter); + GlobalRoutingLinkRecord *linkRemote = 0; + while ((linkRemote = SPFGetNextLink (w, v, linkRemote))) + { +/* ...For each link in the router-LSA that points back to the + * parent network, the link's Link Data field provides the IP + * address of a next hop router. The outgoing interface to + * use can then be derived from the next hop IP address (or + * it can be inherited from the parent network). + */ + w->SetNextHop(linkRemote->GetLinkData ()); + w->SetOutgoingInterfaceId (v->GetOutgoingInterfaceId ()); + NS_DEBUG ("SPFNexthopCalculation: Next hop from " << + v->GetVertexId () << " to " << w->GetVertexId () << + " goes through next hop " << w->GetNextHop () << + " via outgoing interface " << w->GetOutgoingInterfaceId ()); + } + } + else + { + w->SetNextHop (v->GetNextHop ()); + w->SetOutgoingInterfaceId (v->GetOutgoingInterfaceId ()); + } + } + else + { +// +// If we're calculating the next hop information from a node (v) that is +// *not* the root, then we need to "inherit" the information needed to +// forward the packet from the vertex closer to the root. That is, we'll +// still send packets to the next hop address of the router adjacent to the +// root on the path toward . +// +// Above, when we were considering the root node, we calculated the next hop +// address and outgoing interface required to get off of the root network. +// At this point, we are further away from the root network along one of the +// (shortest) paths. So the next hop and outoing interface remain the same +// (are inherited). +// + w->SetNextHop (v->GetNextHop ()); + w->SetOutgoingInterfaceId (v->GetOutgoingInterfaceId ()); + } +// +// In all cases, we need valid values for the distance metric and a parent. +// + w->SetDistanceFromRoot (distance); + w->SetParent (v); + + return 1; +} + +// +// This method is derived from quagga ospf_get_next_link () +// +// First search the Global Router Link Records of vertex for one +// representing a point-to point link to vertex . +// +// What is done depends on prev_link. Contrary to appearances, prev_link just +// acts as a flag here. If prev_link is NULL, we return the first Global +// Router Link Record we find that describes a point-to-point link from +// to . If prev_link is not NULL, we return a Global Router Link Record +// representing a possible *second* link from to . +// + GlobalRoutingLinkRecord* +GlobalRouteManagerImpl::SPFGetNextLink ( + SPFVertex* v, + SPFVertex* w, + GlobalRoutingLinkRecord* prev_link) +{ + NS_DEBUG ("GlobalRouteManagerImpl::SPFGetNextLink ()"); + + bool skip = true; + bool found_prev_link = false; + GlobalRoutingLinkRecord* l; +// +// If prev_link is 0, we are really looking for the first link, not the next +// link. +// + if (prev_link == 0) + { + skip = false; + found_prev_link = true; + } +// +// Iterate through the Global Router Link Records advertised by the vertex +// looking for records representing the point-to-point links off of this +// vertex. +// + for (uint32_t i = 0; i < v->GetLSA ()->GetNLinkRecords (); ++i) + { + l = v->GetLSA ()->GetLinkRecord (i); +// +// The link ID of a link record representing a point-to-point link is set to +// the router ID of the neighboring router -- the router to which the link +// connects from the perspective of in this case. The vertex ID is also +// set to the router ID (using the link state advertisement of a router node). +// We're just checking to see if the link is actually the link from to +// . +// + if (l->GetLinkId () == w->GetVertexId ()) + { + if (!found_prev_link) + { + NS_DEBUG ("SPFGetNextLink: Skipping links before prev_link found"); + found_prev_link = true; + continue; + } + + NS_DEBUG ("SPFGetNextLink: Found matching link l: linkId = " << + l->GetLinkId () << " linkData = " << l->GetLinkData ()); +// +// If skip is false, don't (not too surprisingly) skip the link found -- it's +// the one we're interested in. That's either because we didn't pass in a +// previous link, and we're interested in the first one, or because we've +// skipped a previous link and moved forward to the next (which is then the +// one we want). +// + if (skip == false) + { + NS_DEBUG ("SPFGetNextLink: Returning the found link"); + return l; + } + else + { +// +// Skip is true and we've found a link from to . We want the next one. +// Setting skip to false gets us the next point-to-point global router link +// record in the LSA from . +// + NS_DEBUG ("SPFGetNextLink: Skipping the found link"); + skip = false; + continue; + } + } + } + return 0; +} + +// +// Used for unit tests. +// + void +GlobalRouteManagerImpl::DebugSPFCalculate (Ipv4Address root) +{ + NS_DEBUG ("GlobalRouteManagerImpl::DebugSPFCalculate ()"); + SPFCalculate (root); +} + +// quagga ospf_spf_calculate + void +GlobalRouteManagerImpl::SPFCalculate (Ipv4Address root) +{ + NS_DEBUG ("GlobalRouteManagerImpl::SPFCalculate (): " + "root = " << root); + + SPFVertex *v; +// +// Initialize the Link State Database. +// + m_lsdb->Initialize (); +// +// The candidate queue is a priority queue of SPFVertex objects, with the top +// of the queue being the closest vertex in terms of distance from the root +// of the tree. Initially, this queue is empty. +// + CandidateQueue candidate; + NS_ASSERT(candidate.Size () == 0); +// +// Initialize the shortest-path tree to only contain the router doing the +// calculation. Each router (and corresponding network) is a vertex in the +// shortest path first (SPF) tree. +// + v = new SPFVertex (m_lsdb->GetLSA (root)); +// +// This vertex is the root of the SPF tree and it is distance 0 from the root. +// We also mark this vertex as being in the SPF tree. +// + m_spfroot= v; + v->SetDistanceFromRoot (0); + v->GetLSA ()->SetStatus (GlobalRoutingLSA::LSA_SPF_IN_SPFTREE); + + for (;;) + { +// +// The operations we need to do are given in the OSPF RFC which we reference +// as we go along. +// +// RFC2328 16.1. (2). +// +// We examine the Global Router Link Records in the Link State +// Advertisements of the current vertex. If there are any point-to-point +// links to unexplored adjacent vertices we add them to the tree and update +// the distance and next hop information on how to get there. We also add +// the new vertices to the candidate queue (the priority queue ordered by +// shortest path). If the new vertices represent shorter paths, we use them +// and update the path cost. +// + SPFNext (v, candidate); +// +// RFC2328 16.1. (3). +// +// If at this step the candidate list is empty, the shortest-path tree (of +// transit vertices) has been completely built and this stage of the +// procedure terminates. +// + if (candidate.Size () == 0) + { + break; + } +// +// Choose the vertex belonging to the candidate list that is closest to the +// root, and add it to the shortest-path tree (removing it from the candidate +// list in the process). +// +// Recall that in the previous step, we created SPFVertex structures for each +// of the routers found in the Global Router Link Records and added tehm to +// the candidate list. +// + v = candidate.Pop (); + NS_DEBUG ("SPFCalculate: Popped vertex " << v->GetVertexId ()); +// +// Update the status field of the vertex to indicate that it is in the SPF +// tree. +// + v->GetLSA ()->SetStatus (GlobalRoutingLSA::LSA_SPF_IN_SPFTREE); +// +// The current vertex has a parent pointer. By calling this rather oddly +// named method (blame quagga) we add the current vertex to the list of +// children of that parent vertex. In the next hop calculation called during +// SPFNext, the parent pointer was set but the vertex has been orphaned up +// to now. +// + SPFVertexAddParent (v); +// +// Note that when there is a choice of vertices closest to the root, network +// vertices must be chosen before router vertices in order to necessarily +// find all equal-cost paths. We don't do this at this moment, we should add +// the treatment above codes. -- kunihiro. +// +// RFC2328 16.1. (4). +// +// This is the method that actually adds the routes. It'll walk the list +// of nodes in the system, looking for the node corresponding to the router +// ID of the root of the tree -- that is the router we're building the routes +// for. It looks for the Ipv4 interface of that node and remembers it. So +// we are only actually adding routes to that one node at the root of the SPF +// tree. +// +// We're going to pop of a pointer to every vertex in the tree except the +// root in order of distance from the root. For each of the vertices, we call +// SPFIntraAddRouter (). Down in SPFIntraAddRouter, we look at all of the +// point-to-point Global Router Link Records (the links to nodes adjacent to +// the node represented by the vertex). We add a route to the IP address +// specified by the m_linkData field of each of those link records. This will +// be the *local* IP address associated with the interface attached to the +// link. We use the outbound interface and next hop information present in +// the vertex which have possibly been inherited from the root. +// +// To summarize, we're going to look at the node represented by and loop +// through its point-to-point links, adding a *host* route to the local IP +// address (at the side) for each of those links. +// + if (v->GetVertexType () == SPFVertex::VertexRouter) + { + SPFIntraAddRouter (v); + } + else if (v->GetVertexType () == SPFVertex::VertexNetwork) + { + SPFIntraAddTransit (v); + } + else + { + NS_ASSERT_MSG(0, "illegal SPFVertex type"); + } +// +// RFC2328 16.1. (5). +// +// Iterate the algorithm by returning to Step 2 until there are no more +// candidate vertices. +// + } +// +// Second stage of SPF calculation procedure's +// NOTYET: ospf_spf_process_stubs (area, area->spf, new_table); +// +// We're all done setting the routing information for the node at the root of +// the SPF tree. Delete all of the vertices and corresponding resources. Go +// possibly do it again for the next router. +// + delete m_spfroot; + m_spfroot = 0; +} + +// +// XXX This should probably be a method on Ipv4 +// +// Return the interface index corresponding to a given IP address +// + uint32_t +GlobalRouteManagerImpl::FindOutgoingInterfaceId (Ipv4Address a) +{ +// +// We have an IP address and a vertex ID of the root of the SPF tree. +// The question is what interface index does this address correspond to. +// The answer is a little complicated since we have to find a pointer to +// the node corresponding to the vertex ID, find the Ipv4 interface on that +// node in order to iterate the interfaces and find the one corresponding to +// the address in question. +// + Ipv4Address routerId = m_spfroot->GetVertexId (); +// +// Walk the list of nodes in the system looking for the one corresponding to +// the node at the root of the SPF tree. This is the node for which we are +// building the routing table. +// + NodeList::Iterator i = NodeList::Begin (); + for (; i != NodeList::End (); i++) + { + Ptr node = *i; + + Ptr rtr = + node->QueryInterface (GlobalRouter::iid); +// +// If the node doesn't have a GlobalRouter interface it can't be the one +// we're interested in. +// + if (rtr == 0) + { + continue; + } + + if (rtr->GetRouterId () == routerId) + { +// +// This is the node we're building the routing table for. We're going to need +// the Ipv4 interface to look for the ipv4 interface index. Since this node +// is participating in routing IP version 4 packets, it certainly must have +// an Ipv4 interface. +// + Ptr ipv4 = node->QueryInterface (Ipv4::iid); + NS_ASSERT_MSG (ipv4, + "GlobalRouteManagerImpl::FindOutgoingInterfaceId (): " + "QI for interface failed"); +// +// Look through the interfaces on this node for one that has the IP address +// we're looking for. If we find one, return the corresponding interface +// index. +// + for (uint32_t i = 0; i < ipv4->GetNInterfaces (); i++) + { + if (ipv4->GetAddress (i) == a) + { + NS_DEBUG ( + "GlobalRouteManagerImpl::FindOutgoingInterfaceId (): " + "Interface match for " << a); + return i; + } + } + } + } +// +// Couldn't find it. +// + return 0; +} + +// +// XXX This should probably be a method on Ipv4 +// +// Return the interface index corresponding to a given IP address +// + uint32_t +GlobalRouteManagerImpl::FindOutgoingInterfaceId (Ipv4Address a, Ipv4Mask amask) +{ +// +// We have an IP address and a vertex ID of the root of the SPF tree. +// The question is what interface index does this address correspond to. +// The answer is a little complicated since we have to find a pointer to +// the node corresponding to the vertex ID, find the Ipv4 interface on that +// node in order to iterate the interfaces and find the one corresponding to +// the address in question. +// + Ipv4Address routerId = m_spfroot->GetVertexId (); +// +// Walk the list of nodes in the system looking for the one corresponding to +// the node at the root of the SPF tree. This is the node for which we are +// building the routing table. +// + NodeList::Iterator i = NodeList::Begin (); + for (; i != NodeList::End (); i++) + { + Ptr node = *i; + + Ptr rtr = + node->QueryInterface (GlobalRouter::iid); +// +// If the node doesn't have a GlobalRouter interface it can't be the one +// we're interested in. +// + if (rtr == 0) + { + continue; + } + + if (rtr->GetRouterId () == routerId) + { +// +// This is the node we're building the routing table for. We're going to need +// the Ipv4 interface to look for the ipv4 interface index. Since this node +// is participating in routing IP version 4 packets, it certainly must have +// an Ipv4 interface. +// + Ptr ipv4 = node->QueryInterface (Ipv4::iid); + NS_ASSERT_MSG (ipv4, + "GlobalRouteManagerImpl::FindOutgoingInterfaceId (): " + "QI for interface failed"); +// +// Look through the interfaces on this node for one that has the IP address +// we're looking for. If we find one, return the corresponding interface +// index. +// + for (uint32_t i = 0; i < ipv4->GetNInterfaces (); i++) + { + if (ipv4->GetAddress (i).CombineMask(amask) == + a.CombineMask(amask) ) + { + NS_DEBUG ( + "GlobalRouteManagerImpl::FindOutgoingInterfaceId (): " + "Interface match for " << a); + return i; + } + } + } + } +// +// Couldn't find it. +// + return 0; +} + +// +// This method is derived from quagga ospf_intra_add_router () +// +// This is where we are actually going to add the host routes to the routing +// tables of the individual nodes. +// +// The vertex passed as a parameter has just been added to the SPF tree. +// This vertex must have a valid m_root_oid, corresponding to the outgoing +// interface on the root router of the tree that is the first hop on the path +// to the vertex. The vertex must also have a next hop address, corresponding +// to the next hop on the path to the vertex. The vertex has an m_lsa field +// that has some number of link records. For each point to point link record, +// the m_linkData is the local IP address of the link. This corresponds to +// a destination IP address, reachable from the root, to which we add a host +// route. +// + void +GlobalRouteManagerImpl::SPFIntraAddRouter (SPFVertex* v) +{ + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddRouter ()"); + + NS_ASSERT_MSG (m_spfroot, + "GlobalRouteManagerImpl::SPFIntraAddRouter (): Root pointer not set"); +// +// The root of the Shortest Path First tree is the router to which we are +// going to write the actual routing table entries. The vertex corresponding +// to this router has a vertex ID which is the router ID of that node. We're +// going to use this ID to discover which node it is that we're actually going +// to update. +// + Ipv4Address routerId = m_spfroot->GetVertexId (); + + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddRouter (): " + "Vertex ID = " << routerId); +// +// We need to walk the list of nodes looking for the one that has the router +// ID corresponding to the root vertex. This is the one we're going to write +// the routing information to. +// + NodeList::Iterator i = NodeList::Begin (); + for (; i != NodeList::End (); i++) + { + Ptr node = *i; +// +// The router ID is accessible through the GlobalRouter interface, so we need +// to QI for that interface. If there's no GlobalRouter interface, the node +// in question cannot be the router we want, so we continue. +// + Ptr rtr = + node->QueryInterface (GlobalRouter::iid); + + if (rtr == 0) + { + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddRouter (): " + "No GlobalRouter interface on node " << node->GetId ()); + continue; + } +// +// If the router ID of the current node is equal to the router ID of the +// root of the SPF tree, then this node is the one for which we need to +// write the routing tables. +// + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddRouter (): " + "Considering router " << rtr->GetRouterId ()); + + if (rtr->GetRouterId () == routerId) + { + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddRouter (): " + "setting routes for node " << node->GetId ()); +// +// Routing information is updated using the Ipv4 interface. We need to QI +// for that interface. If the node is acting as an IP version 4 router, it +// should absolutely have an Ipv4 interface. +// + Ptr ipv4 = node->QueryInterface (Ipv4::iid); + NS_ASSERT_MSG (ipv4, + "GlobalRouteManagerImpl::SPFIntraAddRouter (): " + "QI for interface failed"); +// +// Get the Global Router Link State Advertisement from the vertex we're +// adding the routes to. The LSA will have a number of attached Global Router +// Link Records corresponding to links off of that vertex / node. We're going +// to be interested in the records corresponding to point-to-point links. +// + GlobalRoutingLSA *lsa = v->GetLSA (); + NS_ASSERT_MSG (lsa, + "GlobalRouteManagerImpl::SPFIntraAddRouter (): " + "Expected valid LSA in SPFVertex* v"); + + uint32_t nLinkRecords = lsa->GetNLinkRecords (); +// +// Iterate through the link records on the vertex to which we're going to add +// routes. To make sure we're being clear, we're going to add routing table +// entries to the tables on the node corresping to the root of the SPF tree. +// These entries will have routes to the IP addresses we find from looking at +// the local side of the point-to-point links found on the node described by +// the vertex . +// + for (uint32_t j = 0; j < nLinkRecords; j += 2) + { +// +// We are only concerned about point-to-point links +// + GlobalRoutingLinkRecord *lr = lsa->GetLinkRecord (j); + if (lr->GetLinkType () != GlobalRoutingLinkRecord::PointToPoint) + { + continue; + } + + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddRouter (): " + " Node " << node->GetId () << + " add route to " << lr->GetLinkData () << + " using next hop " << v->GetNextHop () << + " via interface " << v->GetOutgoingInterfaceId ()); +// +// Here's why we did all of that work. We're going to add a host route to the +// host address found in the m_linkData field of the point-to-point link +// record. In the case of a point-to-point link, this is the local IP address +// of the node connected to the link. Each of these point-to-point links +// will correspond to a local interface that has an IP address to which +// the node at the root of the SPF tree can send packets. The vertex +// (corresponding to the node that has these links and interfaces) has +// an m_nextHop address precalculated for us that is the address to which the +// root node should send packets to be forwarded to these IP addresses. +// Similarly, the vertex has an m_rootOif (outbound interface index) to +// which the packets should be send for forwarding. +// + ipv4->AddHostRouteTo (lr->GetLinkData (), v->GetNextHop (), + v->GetOutgoingInterfaceId ()); + } +// +// Done adding the routes for the selected node. +// + return; + } + } +} + void +GlobalRouteManagerImpl::SPFIntraAddTransit (SPFVertex* v) +{ + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddTransit ()"); + + NS_ASSERT_MSG (m_spfroot, + "GlobalRouteManagerImpl::SPFIntraAddTransit (): Root pointer not set"); +// +// The root of the Shortest Path First tree is the router to which we are +// going to write the actual routing table entries. The vertex corresponding +// to this router has a vertex ID which is the router ID of that node. We're +// going to use this ID to discover which node it is that we're actually going +// to update. +// + Ipv4Address routerId = m_spfroot->GetVertexId (); + + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddTransit (): " + "Vertex ID = " << routerId); +// +// We need to walk the list of nodes looking for the one that has the router +// ID corresponding to the root vertex. This is the one we're going to write +// the routing information to. +// + NodeList::Iterator i = NodeList::Begin (); + for (; i != NodeList::End (); i++) + { + Ptr node = *i; +// +// The router ID is accessible through the GlobalRouter interface, so we need +// to QI for that interface. If there's no GlobalRouter interface, the node +// in question cannot be the router we want, so we continue. +// + Ptr rtr = + node->QueryInterface (GlobalRouter::iid); + + if (rtr == 0) + { + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddTransit (): " + "No GlobalRouter interface on node " << node->GetId ()); + continue; + } +// +// If the router ID of the current node is equal to the router ID of the +// root of the SPF tree, then this node is the one for which we need to +// write the routing tables. +// + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddTransit (): " + "Considering router " << rtr->GetRouterId ()); + + if (rtr->GetRouterId () == routerId) + { + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddTransit (): " + "setting routes for node " << node->GetId ()); +// +// Routing information is updated using the Ipv4 interface. We need to QI +// for that interface. If the node is acting as an IP version 4 router, it +// should absolutely have an Ipv4 interface. +// + Ptr ipv4 = node->QueryInterface (Ipv4::iid); + NS_ASSERT_MSG (ipv4, + "GlobalRouteManagerImpl::SPFIntraAddTransit (): " + "QI for interface failed"); +// +// Get the Global Router Link State Advertisement from the vertex we're +// adding the routes to. The LSA will have a number of attached Global Router +// Link Records corresponding to links off of that vertex / node. We're going +// to be interested in the records corresponding to point-to-point links. +// + GlobalRoutingLSA *lsa = v->GetLSA (); + NS_ASSERT_MSG (lsa, + "GlobalRouteManagerImpl::SPFIntraAddTransit (): " + "Expected valid LSA in SPFVertex* v"); + Ipv4Mask tempmask = lsa->GetNetworkLSANetworkMask (); + Ipv4Address tempip = lsa->GetLinkStateId (); + tempip = tempip.CombineMask (tempmask); + ipv4->AddNetworkRouteTo (tempip, tempmask, v->GetNextHop (), + v->GetOutgoingInterfaceId ()); + NS_DEBUG ("GlobalRouteManagerImpl::SPFIntraAddNetwork (): " + " Node " << node->GetId () << + " add network route to " << tempip << + " using next hop " << v->GetNextHop () << + " via interface " << v->GetOutgoingInterfaceId ()); + } + } +} + +// Derived from quagga ospf_vertex_add_parents () +// +// This is a somewhat oddly named method (blame quagga). Although you might +// expect it to add a parent *to* something, it actually adds a vertex +// to the list of children *in* each of its parents. +// +// Given a pointer to a vertex, it links back to the vertex's parent that it +// already has set and adds itself to that vertex's list of children. +// +// For now, only one parent (not doing equal-cost multipath) +// + void +GlobalRouteManagerImpl::SPFVertexAddParent (SPFVertex* v) +{ + v->GetParent ()->AddChild (v); +} + +} // namespace ns3 + +#ifdef RUN_SELF_TESTS + +// --------------------------------------------------------------------------- +// +// Unit Tests +// +// --------------------------------------------------------------------------- + +#include "ns3/test.h" +#include "ns3/simulator.h" + +namespace ns3 { + +class GlobalRouterTestNode : public Node +{ +public: + GlobalRouterTestNode (); + +private: + virtual void DoAddDevice (Ptr device) const {}; + virtual TraceResolver *DoCreateTraceResolver (TraceContext const &context); +}; + +GlobalRouterTestNode::GlobalRouterTestNode () +{ +// Ptr ipv4 = Create (this); +} + + TraceResolver* +GlobalRouterTestNode::DoCreateTraceResolver (TraceContext const &context) +{ + return 0; +} + +class GlobalRouteManagerImplTest : public Test { +public: + GlobalRouteManagerImplTest (); + virtual ~GlobalRouteManagerImplTest (); + virtual bool RunTests (void); +}; + +GlobalRouteManagerImplTest::GlobalRouteManagerImplTest () + : Test ("GlobalRouteManagerImpl") +{ +} + +GlobalRouteManagerImplTest::~GlobalRouteManagerImplTest () +{} + + bool +GlobalRouteManagerImplTest::RunTests (void) +{ + bool ok = true; + + CandidateQueue candidate; + + for (int i = 0; i < 100; ++i) + { + SPFVertex *v = new SPFVertex; + v->SetDistanceFromRoot (rand () % 100); + candidate.Push (v); + } + + uint32_t lastDistance = 0; + + for (int i = 0; i < 100; ++i) + { + SPFVertex *v = candidate.Pop (); + if (v->GetDistanceFromRoot () < lastDistance) + { + ok = false; + } + lastDistance = v->GetDistanceFromRoot (); + delete v; + v = 0; + } + + // Build fake link state database; four routers (0-3), 3 point-to-point + // links + // + // n0 + // \ link 0 + // \ link 2 + // n2 -------------------------n3 + // / + // / link 1 + // n1 + // + // link0: 10.1.1.1/30, 10.1.1.2/30 + // link1: 10.1.2.1/30, 10.1.2.2/30 + // link2: 10.1.3.1/30, 10.1.3.2/30 + // + // Router 0 + GlobalRoutingLinkRecord* lr0 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::PointToPoint, + "0.0.0.2", // router ID 0.0.0.2 + "10.1.1.1", // local ID + 1); // metric + + GlobalRoutingLinkRecord* lr1 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::StubNetwork, + "10.1.1.1", + "255.255.255.252", + 1); + + GlobalRoutingLSA* lsa0 = new GlobalRoutingLSA (); + lsa0->SetLSType (GlobalRoutingLSA::RouterLSA); + lsa0->SetLinkStateId ("0.0.0.0"); + lsa0->SetAdvertisingRouter ("0.0.0.0"); + lsa0->AddLinkRecord (lr0); + lsa0->AddLinkRecord (lr1); + + // Router 1 + GlobalRoutingLinkRecord* lr2 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::PointToPoint, + "0.0.0.2", + "10.1.2.1", + 1); + + GlobalRoutingLinkRecord* lr3 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::StubNetwork, + "10.1.2.1", + "255.255.255.252", + 1); + + GlobalRoutingLSA* lsa1 = new GlobalRoutingLSA (); + lsa1->SetLSType (GlobalRoutingLSA::RouterLSA); + lsa1->SetLinkStateId ("0.0.0.1"); + lsa1->SetAdvertisingRouter ("0.0.0.1"); + lsa1->AddLinkRecord (lr2); + lsa1->AddLinkRecord (lr3); + + // Router 2 + GlobalRoutingLinkRecord* lr4 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::PointToPoint, + "0.0.0.0", + "10.1.1.2", + 1); + + GlobalRoutingLinkRecord* lr5 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::StubNetwork, + "10.1.1.2", + "255.255.255.252", + 1); + + GlobalRoutingLinkRecord* lr6 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::PointToPoint, + "0.0.0.1", + "10.1.2.2", + 1); + + GlobalRoutingLinkRecord* lr7 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::StubNetwork, + "10.1.2.2", + "255.255.255.252", + 1); + + GlobalRoutingLinkRecord* lr8 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::PointToPoint, + "0.0.0.3", + "10.1.3.2", + 1); + + GlobalRoutingLinkRecord* lr9 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::StubNetwork, + "10.1.3.2", + "255.255.255.252", + 1); + + GlobalRoutingLSA* lsa2 = new GlobalRoutingLSA (); + lsa2->SetLSType (GlobalRoutingLSA::RouterLSA); + lsa2->SetLinkStateId ("0.0.0.2"); + lsa2->SetAdvertisingRouter ("0.0.0.2"); + lsa2->AddLinkRecord (lr4); + lsa2->AddLinkRecord (lr5); + lsa2->AddLinkRecord (lr6); + lsa2->AddLinkRecord (lr7); + lsa2->AddLinkRecord (lr8); + lsa2->AddLinkRecord (lr9); + + // Router 3 + GlobalRoutingLinkRecord* lr10 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::PointToPoint, + "0.0.0.2", + "10.1.2.1", + 1); + + GlobalRoutingLinkRecord* lr11 = new GlobalRoutingLinkRecord ( + GlobalRoutingLinkRecord::StubNetwork, + "10.1.2.1", + "255.255.255.252", + 1); + + GlobalRoutingLSA* lsa3 = new GlobalRoutingLSA (); + lsa3->SetLSType (GlobalRoutingLSA::RouterLSA); + lsa3->SetLinkStateId ("0.0.0.3"); + lsa3->SetAdvertisingRouter ("0.0.0.3"); + lsa3->AddLinkRecord (lr10); + lsa3->AddLinkRecord (lr11); + + // Test the database + GlobalRouteManagerLSDB* srmlsdb = new GlobalRouteManagerLSDB (); + srmlsdb->Insert (lsa0->GetLinkStateId (), lsa0); + srmlsdb->Insert (lsa1->GetLinkStateId (), lsa1); + srmlsdb->Insert (lsa2->GetLinkStateId (), lsa2); + srmlsdb->Insert (lsa3->GetLinkStateId (), lsa3); + NS_ASSERT (lsa2 == srmlsdb->GetLSA (lsa2->GetLinkStateId ())); + + // next, calculate routes based on the manually created LSDB + GlobalRouteManagerImpl* srm = new GlobalRouteManagerImpl (); + srm->DebugUseLsdb (srmlsdb); // manually add in an LSDB + // Note-- this will succeed without any nodes in the topology + // because the NodeList is empty + srm->DebugSPFCalculate (lsa0->GetLinkStateId ()); // node n0 + + Simulator::Run (); + Simulator::Destroy (); + + // This delete clears the srm, which deletes the LSDB, which clears + // all of the LSAs, which each destroys the attached LinkRecords. + delete srm; + + return ok; +} + +// Instantiate this class for the unit tests +// XXX here we should do some verification of the routes built +static GlobalRouteManagerImplTest g_globalRouteManagerTest; + +} // namespace ns3 + +#endif diff --git a/src/routing/global-routing/global-route-manager-impl.h b/src/routing/global-routing/global-route-manager-impl.h new file mode 100644 index 000000000..739e20592 --- /dev/null +++ b/src/routing/global-routing/global-route-manager-impl.h @@ -0,0 +1,763 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#ifndef GLOBAL_ROUTE_MANAGER_IMPL_H +#define GLOBAL_ROUTE_MANAGER_IMPL_H + +#include +#include +#include +#include +#include "ns3/object.h" +#include "ns3/ptr.h" +#include "ns3/ipv4-address.h" +#include "global-router-interface.h" + +namespace ns3 { + +const uint32_t SPF_INFINITY = 0xffffffff; + +class CandidateQueue; + +/** + * @brief Vertex used in shortest path first (SPF) computations. See RFC 2328, + * Section 16. + * + * Each router in the simulation is associated with an SPFVertex object. When + * calculating routes, each of these routers is, in turn, chosen as the "root" + * of the calculation and routes to all of the other routers are eventually + * saved in the routing tables of each of the chosen nodes. Each of these + * routers in the calculation has an associated SPFVertex. + * + * The "Root" vertex is the SPFVertex representing the router that is having + * its routing tables set. The SPFVertex objects representing other routers + * or networks in the simulation are arranged in the SPF tree. It is this + * tree that represents the Shortest Paths to the other networks. + * + * Each SPFVertex has a pointer to the Global Router Link State Advertisement + * (LSA) that its underlying router has exported. Within these LSAs are + * Global Router Link Records that describe the point to point links from the + * underlying router to other nodes (represented by other SPFVertex objects) + * in the simulation topology. The combination of the arrangement of the + * SPFVertex objects in the SPF tree, along with the details of the link + * records that connect them provide the information required to construct the + * required routes. + */ +class SPFVertex +{ +public: +/** + * @brief Enumeration of the possible types of SPFVertex objects. + * @internal + * + * Currently we use VertexRouter to identify objects that represent a router + * in the simulation topology, and VertexNetwork to identify objects that + * represent a network. + */ + enum VertexType { + VertexUnknown = 0, /**< Uninitialized Link Record */ + VertexRouter, /**< Vertex representing a router in the topology */ + VertexNetwork /**< Vertex representing a network in the topology */ + }; + +/** + * @brief Construct an empty ("uninitialized") SPFVertex (Shortest Path First + * Vertex). + * @internal + * + * The Vertex Type is set to VertexUnknown, the Vertex ID is set to + * 255.255.255.255, and the distance from root is set to infinity + * (UINT32_MAX). The referenced Link State Advertisement (LSA) is set to + * null as is the parent SPFVertex. The outgoing interface index is set to + * infinity, the next hop address is set to 0.0.0.0 and the list of children + * of the SPFVertex is initialized to empty. + * + * @see VertexType + */ + SPFVertex(); + +/** + * @brief Construct an initialized SPFVertex (Shortest Path First Vertex). + * @internal + * + * The Vertex Type is initialized to VertexRouter and the Vertex ID is found + * from the Link State ID of the Link State Advertisement (LSA) passed as a + * parameter. The Link State ID is set to the Router ID of the advertising + * router. The referenced LSA (m_lsa) is set to the given LSA. Other than + * these members, initialization is as in the default constructor. + * of the SPFVertex is initialized to empty. + * + * @see SPFVertex::SPFVertex () + * @see VertexType + * @see GlobalRoutingLSA + * @param lsa The Link State Advertisement used for finding initial values. + */ + SPFVertex(GlobalRoutingLSA* lsa); + +/** + * @brief Destroy an SPFVertex (Shortest Path First Vertex). + * @internal + * + * The children vertices of the SPFVertex are recursively deleted. + * + * @see SPFVertex::SPFVertex () + */ + ~SPFVertex(); + +/** + * @brief Get the Vertex Type field of a SPFVertex object. + * @internal + * + * The Vertex Type describes the kind of simulation object a given SPFVertex + * represents. + * + * @see VertexType + * @returns The VertexType of the current SPFVertex object. + */ + VertexType GetVertexType (void) const; + +/** + * @brief Set the Vertex Type field of a SPFVertex object. + * @internal + * + * The Vertex Type describes the kind of simulation object a given SPFVertex + * represents. + * + * @see VertexType + * @param type The new VertexType for the current SPFVertex object. + */ + void SetVertexType (VertexType type); + +/** + * @brief Get the Vertex ID field of a SPFVertex object. + * @internal + * + * The Vertex ID uniquely identifies the simulation object a given SPFVertex + * represents. Typically, this is the Router ID for SPFVertex objects + * representing routers, and comes from the Link State Advertisement of a + * router aggregated to a node in the simulation. These IDs are allocated + * automatically by the routing environment and look like IP addresses + * beginning at 0.0.0.0 and monotonically increasing as new routers are + * instantiated. + * + * @returns The Ipv4Address Vertex ID of the current SPFVertex object. + */ + Ipv4Address GetVertexId (void) const; + +/** + * @brief Set the Vertex ID field of a SPFVertex object. + * @internal + * + * The Vertex ID uniquely identifies the simulation object a given SPFVertex + * represents. Typically, this is the Router ID for SPFVertex objects + * representing routers, and comes from the Link State Advertisement of a + * router aggregated to a node in the simulation. These IDs are allocated + * automatically by the routing environment and look like IP addresses + * beginning at 0.0.0.0 and monotonically increase as new routers are + * instantiated. This method is an explicit override of the automatically + * generated value. + * + * @param id The new Ipv4Address Vertex ID for the current SPFVertex object. + */ + void SetVertexId (Ipv4Address id); + +/** + * @brief Get the Global Router Link State Advertisement returned by the + * Global Router represented by this SPFVertex during the route discovery + * process. + * @internal + * + * @see GlobalRouter + * @see GlobalRoutingLSA + * @see GlobalRouter::DiscoverLSAs () + * @returns A pointer to the GlobalRoutingLSA found by the router represented + * by this SPFVertex object. + */ + GlobalRoutingLSA* GetLSA (void) const; + +/** + * @brief Set the Global Router Link State Advertisement returned by the + * Global Router represented by this SPFVertex during the route discovery + * process. + * @internal + * + * @see SPFVertex::GetLSA () + * @see GlobalRouter + * @see GlobalRoutingLSA + * @see GlobalRouter::DiscoverLSAs () + * @warning Ownership of the LSA is transferred to the "this" SPFVertex. You + * must not delete the LSA after calling this method. + * @param lsa A pointer to the GlobalRoutingLSA. + */ + void SetLSA (GlobalRoutingLSA* lsa); + +/** + * @brief Get the distance from the root vertex to "this" SPFVertex object. + * @internal + * + * Each router in the simulation is associated with an SPFVertex object. When + * calculating routes, each of these routers is, in turn, chosen as the "root" + * of the calculation and routes to all of the other routers are eventually + * saved in the routing tables of each of the chosen nodes. Each of these + * routers in the calculation has an associated SPFVertex. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set. The "this" SPFVertex is the vertex to which + * a route is being calculated from the root. The distance from the root that + * we're asking for is the number of hops from the root vertex to the vertex + * in question. + * + * The distance is calculated during route discovery and is stored in a + * member variable. This method simply fetches that value. + * + * @returns The distance, in hops, from the root SPFVertex to "this" SPFVertex. + */ + uint32_t GetDistanceFromRoot (void) const; + +/** + * @brief Set the distance from the root vertex to "this" SPFVertex object. + * @internal + * + * Each router in the simulation is associated with an SPFVertex object. When + * calculating routes, each of these routers is, in turn, chosen as the "root" + * of the calculation and routes to all of the other routers are eventually + * saved in the routing tables of each of the chosen nodes. Each of these + * routers in the calculation has an associated SPFVertex. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set. The "this" SPFVertex is the vertex to which + * a route is being calculated from the root. The distance from the root that + * we're asking for is the number of hops from the root vertex to the vertex + * in question. + * + * @param distance The distance, in hops, from the root SPFVertex to "this" + * SPFVertex. + */ + void SetDistanceFromRoot (uint32_t distance); + +/** + * @brief Get the interface ID that should be used to begin forwarding packets + * from the root SPFVertex to "this" SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set. The "this" SPFVertex is the vertex that + * represents the host or network to which a route is being calculated from + * the root. The outgoing interface that we're asking for is the interface + * index on the root node that should be used to start packets along the + * path to "this" vertex. + * + * When initializing the root SPFVertex, the interface ID is determined by + * examining the Global Router Link Records of the Link State Advertisement + * generated by the root node's GlobalRouter. These interfaces are used to + * forward packets off of the root's network down those links. As other + * vertices are discovered which are further away from the root, they will + * be accessible down one of the paths begun by a Global Router Link Record. + * + * To forward packets to these hosts or networks, the root node must begin + * the forwarding process by sending the packets to the interface of that + * first hop. This means that the first hop address and interface ID must + * be the same for all downstream SPFVertices. We call this "inheriting" + * the interface and next hop. + * + * In this method, the root node is asking, "which of my local interfaces + * should I use to get a packet to the network or host represented by 'this' + * SPFVertex." + * + * @see GlobalRouter + * @see GlobalRoutingLSA + * @see GlobalRoutingLinkRecord + * @returns The interface index to use when forwarding packets to the host + * or network represented by "this" SPFVertex. + */ + uint32_t GetOutgoingInterfaceId (void) const; + +/** + * @brief Set the interface ID that should be used to begin forwarding packets + * from the root SPFVertex to "this" SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set. The "this" SPFVertex is the vertex that + * represents the host or network to which a route is being calculated from + * the root. The outgoing interface that we're asking for is the interface + * index on the root node that should be used to start packets along the + * path to "this" vertex. + * + * When initializing the root SPFVertex, the interface ID is determined by + * examining the Global Router Link Records of the Link State Advertisement + * generated by the root node's GlobalRouter. These interfaces are used to + * forward packets off of the root's network down those links. As other + * vertices are discovered which are further away from the root, they will + * be accessible down one of the paths begun by a Global Router Link Record. + * + * To forward packets to these hosts or networks, the root node must begin + * the forwarding process by sending the packets to the interface of that + * first hop. This means that the first hop address and interface ID must + * be the same for all downstream SPFVertices. We call this "inheriting" + * the interface and next hop. + * + * In this method, we are letting the root node know which of its local + * interfaces it should use to get a packet to the network or host represented + * by "this" SPFVertex. + * + * @see GlobalRouter + * @see GlobalRoutingLSA + * @see GlobalRoutingLinkRecord + * @param id The interface index to use when forwarding packets to the host or + * network represented by "this" SPFVertex. + */ + void SetOutgoingInterfaceId (uint32_t id); + +/** + * @brief Get the IP address that should be used to begin forwarding packets + * from the root SPFVertex to "this" SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set. The "this" SPFVertex is the vertex that + * represents the host or network to which a route is being calculated from + * the root. The IP address that we're asking for is the address on the + * remote side of a link off of the root node that should be used as the + * destination for packets along the path to "this" vertex. + * + * When initializing the root SPFVertex, the IP address used when forwarding + * packets is determined by examining the Global Router Link Records of the + * Link State Advertisement generated by the root node's GlobalRouter. This + * address is used to forward packets off of the root's network down those + * links. As other vertices / nodes are discovered which are further away + * from the root, they will be accessible down one of the paths via a link + * described by one of these Global Router Link Records. + * + * To forward packets to these hosts or networks, the root node must begin + * the forwarding process by sending the packets to a first hop router down + * an interface. This means that the first hop address and interface ID must + * be the same for all downstream SPFVertices. We call this "inheriting" + * the interface and next hop. + * + * In this method, the root node is asking, "which router should I send a + * packet to in order to get that packet to the network or host represented + * by 'this' SPFVertex." + * + * @see GlobalRouter + * @see GlobalRoutingLSA + * @see GlobalRoutingLinkRecord + * @returns The IP address to use when forwarding packets to the host + * or network represented by "this" SPFVertex. + */ + Ipv4Address GetNextHop (void) const; + +/** + * @brief Set the IP address that should be used to begin forwarding packets + * from the root SPFVertex to "this" SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set. The "this" SPFVertex is the vertex that + * represents the host or network to which a route is being calculated from + * the root. The IP address that we're asking for is the address on the + * remote side of a link off of the root node that should be used as the + * destination for packets along the path to "this" vertex. + * + * When initializing the root SPFVertex, the IP address used when forwarding + * packets is determined by examining the Global Router Link Records of the + * Link State Advertisement generated by the root node's GlobalRouter. This + * address is used to forward packets off of the root's network down those + * links. As other vertices / nodes are discovered which are further away + * from the root, they will be accessible down one of the paths via a link + * described by one of these Global Router Link Records. + * + * To forward packets to these hosts or networks, the root node must begin + * the forwarding process by sending the packets to a first hop router down + * an interface. This means that the first hop address and interface ID must + * be the same for all downstream SPFVertices. We call this "inheriting" + * the interface and next hop. + * + * In this method we are telling the root node which router it should send + * should I send a packet to in order to get that packet to the network or + * host represented by 'this' SPFVertex." + * + * @see GlobalRouter + * @see GlobalRoutingLSA + * @see GlobalRoutingLinkRecord + * @param nextHop The IP address to use when forwarding packets to the host + * or network represented by "this" SPFVertex. + */ + void SetNextHop (Ipv4Address nextHop); + +/** + * @brief Get a pointer to the SPFVector that is the parent of "this" + * SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set and is the root of the SPF tree. + * + * This method returns a pointer to the parent node of "this" SPFVertex + * (both of which reside in that SPF tree). + * + * @returns A pointer to the SPFVertex that is the parent of "this" SPFVertex + * in the SPF tree. + */ + SPFVertex* GetParent (void) const; + +/** + * @brief Set the pointer to the SPFVector that is the parent of "this" + * SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set and is the root of the SPF tree. + * + * This method sets the parent pointer of "this" SPFVertex (both of which + * reside in that SPF tree). + * + * @param parent A pointer to the SPFVertex that is the parent of "this" + * SPFVertex* in the SPF tree. + */ + void SetParent (SPFVertex* parent); + +/** + * @brief Get the number of children of "this" SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set and is the root of the SPF tree. Each vertex + * in the SPF tree can have a number of children that represent host or + * network routes available via that vertex. + * + * This method returns the number of children of "this" SPFVertex (which + * reside in the SPF tree). + * + * @returns The number of children of "this" SPFVertex (which reside in the + * SPF tree). + */ + uint32_t GetNChildren (void) const; + +/** + * @brief Get a borrowed SPFVertex pointer to the specified child of "this" + * SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set and is the root of the SPF tree. Each vertex + * in the SPF tree can have a number of children that represent host or + * network routes available via that vertex. + * + * This method the number of children of "this" SPFVertex (which reside in + * the SPF tree. + * + * @see SPFVertex::GetNChildren + * @param n The index (from 0 to the number of children minus 1) of the + * child SPFVertex to return. + * @warning The pointer returned by GetChild () is a borrowed pointer. You + * do not have any ownership of the underlying object and must not delete + * that object. + * @returns A pointer to the specified child SPFVertex (which resides in the + * SPF tree). + */ + SPFVertex* GetChild (uint32_t n) const; + +/** + * @brief Get a borrowed SPFVertex pointer to the specified child of "this" + * SPFVertex. + * @internal + * + * Each router node in the simulation is associated with an SPFVertex object. + * When calculating routes, each of these routers is, in turn, chosen as the + * "root" of the calculation and routes to all of the other routers are + * eventually saved in the routing tables of each of the chosen nodes. + * + * The "Root" vertex is then the SPFVertex representing the router that is + * having its routing tables set and is the root of the SPF tree. Each vertex + * in the SPF tree can have a number of children that represent host or + * network routes available via that vertex. + * + * This method the number of children of "this" SPFVertex (which reside in + * the SPF tree. + * + * @see SPFVertex::GetNChildren + * @warning Ownership of the pointer added to the children of "this" + * SPFVertex is transferred to the "this" SPFVertex. You must not delete the + * (now) child SPFVertex after calling this method. + * @param child A pointer to the SPFVertex (which resides in the SPF tree) to + * be added to the list of children of "this" SPFVertex. + * @returns The number of children of "this" SPFVertex after the addition of + * the new child. + */ + uint32_t AddChild (SPFVertex* child); + +private: + VertexType m_vertexType; + Ipv4Address m_vertexId; + GlobalRoutingLSA* m_lsa; + uint32_t m_distanceFromRoot; + uint32_t m_rootOif; + Ipv4Address m_nextHop; + SPFVertex* m_parent; + typedef std::list ListOfSPFVertex_t; + ListOfSPFVertex_t m_children; + +/** + * @brief The SPFVertex copy construction is disallowed. There's no need for + * it and a compiler provided shallow copy would be wrong. + */ + SPFVertex (SPFVertex& v); + +/** + * @brief The SPFVertex copy assignment operator is disallowed. There's no + * need for it and a compiler provided shallow copy would be wrong. + */ + SPFVertex& operator= (SPFVertex& v); +}; + +/** + * @brief The Link State DataBase (LSDB) of the Global Route Manager. + * + * Each node in the simulation participating in global routing has a + * GlobalRouter interface. The primary job of this interface is to export + * Global Router Link State Advertisements (LSAs). These advertisements in + * turn contain a number of Global Router Link Records that describe the + * point to point links from the underlying node to other nodes (that will + * also export their own LSAs. + * + * This class implements a searchable database of LSAs gathered from every + * router in the simulation. + */ +class GlobalRouteManagerLSDB +{ +public: +/** + * @brief Construct an empty Global Router Manager Link State Database. + * @internal + * + * The database map composing the Link State Database is initialized in + * this constructor. + */ + GlobalRouteManagerLSDB (); + +/** + * @brief Destroy an empty Global Router Manager Link State Database. + * @internal + * + * The database map is walked and all of the Link State Advertisements stored + * in the database are freed; then the database map itself is clear ()ed to + * release any remaining resources. + */ + ~GlobalRouteManagerLSDB (); + +/** + * @brief Insert an IP address / Link State Advertisement pair into the Link + * State Database. + * @internal + * + * The IPV4 address and the GlobalRoutingLSA given as parameters are converted + * to an STL pair and are inserted into the database map. + * + * @see GlobalRoutingLSA + * @see Ipv4Address + * @param addr The IP address associated with the LSA. Typically the Router + * ID. + * @param lsa A pointer to the Link State Advertisement for the router. + */ + void Insert(Ipv4Address addr, GlobalRoutingLSA* lsa); + +/** + * @brief Look up the Link State Advertisement associated with the given + * link state ID (address). + * @internal + * + * The database map is searched for the given IPV4 address and corresponding + * GlobalRoutingLSA is returned. + * + * @see GlobalRoutingLSA + * @see Ipv4Address + * @param addr The IP address associated with the LSA. Typically the Router + * ID. + * @returns A pointer to the Link State Advertisement for the router specified + * by the IP address addr. + */ + GlobalRoutingLSA* GetLSA (Ipv4Address addr) const; +/** + * @brief Look up the Link State Advertisement associated with the given + * link state ID (address). This is a variation of the GetLSA call + * to allow the LSA to be found by matching addr with the LinkData field + * of the TransitNetwork link record. + * @internal + * + * @see GetLSA + * @param addr The IP address associated with the LSA. Typically the Router + * @returns A pointer to the Link State Advertisement for the router specified + * by the IP address addr. + * ID. + */ + GlobalRoutingLSA* GetLSAByLinkData (Ipv4Address addr) const; + +/** + * @brief Set all LSA flags to an initialized state, for SPF computation + * @internal + * + * This function walks the database and resets the status flags of all of the + * contained Link State Advertisements to LSA_SPF_NOT_EXPLORED. This is done + * prior to each SPF calculation to reset the state of the SPFVertex structures + * that will reference the LSAs during the calculation. + * + * @see GlobalRoutingLSA + * @see SPFVertex + */ + void Initialize (); + +private: + typedef std::map LSDBMap_t; + typedef std::pair LSDBPair_t; + + LSDBMap_t m_database; +/** + * @brief GlobalRouteManagerLSDB copy construction is disallowed. There's no + * need for it and a compiler provided shallow copy would be wrong. + */ + GlobalRouteManagerLSDB (GlobalRouteManagerLSDB& lsdb); + +/** + * @brief The SPFVertex copy assignment operator is disallowed. There's no + * need for it and a compiler provided shallow copy would be wrong. + */ + GlobalRouteManagerLSDB& operator= (GlobalRouteManagerLSDB& lsdb); +}; + +/** + * @brief A global router implementation. + * + * This singleton object can query interface each node in the system + * for a GlobalRouter interface. For those nodes, it fetches one or + * more Link State Advertisements and stores them in a local database. + * Then, it can compute shortest paths on a per-node basis to all routers, + * and finally configure each of the node's forwarding tables. + * + * The design is guided by OSPFv2 RFC 2328 section 16.1.1 and quagga ospfd. + */ +class GlobalRouteManagerImpl +{ +public: + GlobalRouteManagerImpl (); + virtual ~GlobalRouteManagerImpl (); +/** + * @brief Select which nodes in the system are to be router nodes and + * aggregate the appropriate interfaces onto those nodes. + * @internal + * + */ + virtual void SelectRouterNodes (); + +/** + * @brief Build the routing database by gathering Link State Advertisements + * from each node exporting a GlobalRouter interface. + * @internal + */ + virtual void BuildGlobalRoutingDatabase (); + +/** + * @brief Compute routes using a Dijkstra SPF computation and populate + * per-node forwarding tables + * @internal + */ + virtual void InitializeRoutes (); + +/** + * @brief Debugging routine; allow client code to supply a pre-built LSDB + * @internal + */ + void DebugUseLsdb (GlobalRouteManagerLSDB*); + +/** + * @brief Debugging routine; call the core SPF from the unit tests + * @internal + */ + void DebugSPFCalculate (Ipv4Address root); + +private: +/** + * @brief GlobalRouteManagerImpl copy construction is disallowed. + * There's no need for it and a compiler provided shallow copy would be + * wrong. + */ + GlobalRouteManagerImpl (GlobalRouteManagerImpl& srmi); + +/** + * @brief Global Route Manager Implementation assignment operator is + * disallowed. There's no need for it and a compiler provided shallow copy + * would be hopelessly wrong. + */ + GlobalRouteManagerImpl& operator= (GlobalRouteManagerImpl& srmi); + + SPFVertex* m_spfroot; + GlobalRouteManagerLSDB* m_lsdb; + void SPFCalculate (Ipv4Address root); + void SPFNext (SPFVertex*, CandidateQueue&); + int SPFNexthopCalculation (SPFVertex* v, SPFVertex* w, + GlobalRoutingLinkRecord* l, uint32_t distance); + void SPFVertexAddParent (SPFVertex* v); + GlobalRoutingLinkRecord* SPFGetNextLink (SPFVertex* v, SPFVertex* w, + GlobalRoutingLinkRecord* prev_link); + void SPFIntraAddRouter (SPFVertex* v); + void SPFIntraAddTransit (SPFVertex* v); + uint32_t FindOutgoingInterfaceId (Ipv4Address a); + uint32_t FindOutgoingInterfaceId (Ipv4Address a, Ipv4Mask amask); +}; + +} // namespace ns3 + +#endif /* GLOBAL_ROUTE_MANAGER_IMPL_H */ diff --git a/src/routing/global-routing/global-route-manager.cc b/src/routing/global-routing/global-route-manager.cc new file mode 100644 index 000000000..c166da015 --- /dev/null +++ b/src/routing/global-routing/global-route-manager.cc @@ -0,0 +1,68 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#include "ns3/assert.h" +#include "ns3/debug.h" +#include "ns3/simulation-singleton.h" +#include "global-route-manager.h" +#include "global-route-manager-impl.h" + +namespace ns3 { + +// --------------------------------------------------------------------------- +// +// GlobalRouteManager Implementation +// +// --------------------------------------------------------------------------- + + void +GlobalRouteManager::PopulateRoutingTables () +{ + SelectRouterNodes (); + BuildGlobalRoutingDatabase (); + InitializeRoutes (); +} + + void +GlobalRouteManager::SelectRouterNodes () +{ + SimulationSingleton::Get ()-> + SelectRouterNodes (); +} + + void +GlobalRouteManager::BuildGlobalRoutingDatabase () +{ + SimulationSingleton::Get ()-> + BuildGlobalRoutingDatabase (); +} + + void +GlobalRouteManager::InitializeRoutes () +{ + SimulationSingleton::Get ()-> + InitializeRoutes (); +} + + uint32_t +GlobalRouteManager::AllocateRouterId () +{ + static uint32_t routerId = 0; + return routerId++; +} + + +} // namespace ns3 diff --git a/src/routing/global-routing/global-route-manager.h b/src/routing/global-routing/global-route-manager.h new file mode 100644 index 000000000..7562da67d --- /dev/null +++ b/src/routing/global-routing/global-route-manager.h @@ -0,0 +1,93 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#ifndef GLOBAL_ROUTE_MANAGER_H +#define GLOBAL_ROUTE_MANAGER_H + +namespace ns3 { + +/** + * @brief A global global router + * + * This singleton object can query interface each node in the system + * for a GlobalRouter interface. For those nodes, it fetches one or + * more Link State Advertisements and stores them in a local database. + * Then, it can compute shortest paths on a per-node basis to all routers, + * and finally configure each of the node's forwarding tables. + * + * The design is guided by OSPFv2 RFC 2328 section 16.1.1 and quagga ospfd. + */ +class GlobalRouteManager +{ +public: +/** + * @brief Build a routing database and initialize the routing tables of + * the nodes in the simulation. + * + * All this function does is call BuildGlobalRoutingDatabase () and + * InitializeRoutes (). + * + * @see BuildGlobalRoutingDatabase (); + * @see InitializeRoutes (); + */ + static void PopulateRoutingTables (); + +/** + * @brief Allocate a 32-bit router ID from monotonically increasing counter. + */ + static uint32_t AllocateRouterId (); + +private: +/** + * @brief Select which nodes in the system are to be router nodes and + * aggregate the appropriate interfaces onto those nodes. + * @internal + * + */ + static void SelectRouterNodes (); + +/** + * @brief Build the routing database by gathering Link State Advertisements + * from each node exporting a GlobalRouter interface. + * @internal + * + */ + static void BuildGlobalRoutingDatabase (); + +/** + * @brief Compute routes using a Dijkstra SPF computation and populate + * per-node forwarding tables + * @internal + */ + static void InitializeRoutes (); + +/** + * @brief Global Route Manager copy construction is disallowed. There's no + * need for it and a compiler provided shallow copy would be wrong. + * + */ + GlobalRouteManager (GlobalRouteManager& srm); + +/** + * @brief Global Router copy assignment operator is disallowed. There's no + * need for it and a compiler provided shallow copy would be wrong. + */ + GlobalRouteManager& operator= (GlobalRouteManager& srm); +}; + +} // namespace ns3 + +#endif /* GLOBAL_ROUTE_MANAGER_H */ diff --git a/src/routing/global-routing/global-router-interface.cc b/src/routing/global-routing/global-router-interface.cc new file mode 100644 index 000000000..c057f9ddf --- /dev/null +++ b/src/routing/global-routing/global-router-interface.cc @@ -0,0 +1,821 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#include "ns3/debug.h" +#include "ns3/assert.h" +#include "ns3/channel.h" +#include "ns3/net-device.h" +#include "ns3/internet-node.h" +#include "ns3/ipv4.h" +#include "global-router-interface.h" + +NS_DEBUG_COMPONENT_DEFINE ("GlobalRouter"); + +namespace ns3 { + +// --------------------------------------------------------------------------- +// +// GlobalRoutingLinkRecord Implementation +// +// --------------------------------------------------------------------------- + +GlobalRoutingLinkRecord::GlobalRoutingLinkRecord () +: + m_linkId ("0.0.0.0"), + m_linkData ("0.0.0.0"), + m_linkType (Unknown), + m_metric (0) +{ + NS_DEBUG("GlobalRoutingLinkRecord::GlobalRoutingLinkRecord ()"); +} + +GlobalRoutingLinkRecord::GlobalRoutingLinkRecord ( + LinkType linkType, + Ipv4Address linkId, + Ipv4Address linkData, + uint32_t metric) +: + m_linkId (linkId), + m_linkData (linkData), + m_linkType (linkType), + m_metric (metric) +{ + NS_DEBUG("GlobalRoutingLinkRecord::GlobalRoutingLinkRecord (" << + linkType << ", " << linkId << ", " << linkData << ", " << metric << ")"); +} + +GlobalRoutingLinkRecord::~GlobalRoutingLinkRecord () +{ + NS_DEBUG("GlobalRoutingLinkRecord::~GlobalRoutingLinkRecord ()"); +} + + Ipv4Address +GlobalRoutingLinkRecord::GetLinkId (void) const +{ + NS_DEBUG("GlobalRoutingLinkRecord::GetLinkId ()"); + return m_linkId; +} + + void +GlobalRoutingLinkRecord::SetLinkId (Ipv4Address addr) +{ + NS_DEBUG("GlobalRoutingLinkRecord::SetLinkId ()"); + m_linkId = addr; +} + + Ipv4Address +GlobalRoutingLinkRecord::GetLinkData (void) const +{ + NS_DEBUG("GlobalRoutingLinkRecord::GetLinkData ()"); + return m_linkData; +} + + void +GlobalRoutingLinkRecord::SetLinkData (Ipv4Address addr) +{ + NS_DEBUG("GlobalRoutingLinkRecord::SetLinkData ()"); + m_linkData = addr; +} + + GlobalRoutingLinkRecord::LinkType +GlobalRoutingLinkRecord::GetLinkType (void) const +{ + NS_DEBUG("GlobalRoutingLinkRecord::GetLinkType ()"); + return m_linkType; +} + + void +GlobalRoutingLinkRecord::SetLinkType ( + GlobalRoutingLinkRecord::LinkType linkType) +{ + NS_DEBUG("GlobalRoutingLinkRecord::SetLinkType ()"); + m_linkType = linkType; +} + + uint32_t +GlobalRoutingLinkRecord::GetMetric (void) const +{ + NS_DEBUG("GlobalRoutingLinkRecord::GetMetric ()"); + return m_metric; +} + + void +GlobalRoutingLinkRecord::SetMetric (uint32_t metric) +{ + NS_DEBUG("GlobalRoutingLinkRecord::SetMetric ()"); + m_metric = metric; +} + +// --------------------------------------------------------------------------- +// +// GlobalRoutingLSA Implementation +// +// --------------------------------------------------------------------------- + +GlobalRoutingLSA::GlobalRoutingLSA() + : + m_lsType (GlobalRoutingLSA::Unknown), + m_linkStateId("0.0.0.0"), + m_advertisingRtr("0.0.0.0"), + m_linkRecords(), + m_networkLSANetworkMask("0.0.0.0"), + m_attachedRouters(), + m_status(GlobalRoutingLSA::LSA_SPF_NOT_EXPLORED) +{ + NS_DEBUG("GlobalRoutingLSA::GlobalRoutingLSA ()"); +} + +GlobalRoutingLSA::GlobalRoutingLSA ( + GlobalRoutingLSA::SPFStatus status, + Ipv4Address linkStateId, + Ipv4Address advertisingRtr) +: + m_lsType (GlobalRoutingLSA::Unknown), + m_linkStateId(linkStateId), + m_advertisingRtr(advertisingRtr), + m_linkRecords(), + m_networkLSANetworkMask("0.0.0.0"), + m_attachedRouters(), + m_status(status) +{ + NS_DEBUG("GlobalRoutingLSA::GlobalRoutingLSA (" << status << ", " << + linkStateId << ", " << advertisingRtr << ")"); +} + +GlobalRoutingLSA::GlobalRoutingLSA (GlobalRoutingLSA& lsa) + : m_lsType(lsa.m_lsType), m_linkStateId(lsa.m_linkStateId), + m_advertisingRtr(lsa.m_advertisingRtr), + m_networkLSANetworkMask(lsa.m_networkLSANetworkMask), + m_status(lsa.m_status) +{ + NS_ASSERT_MSG(IsEmpty(), + "GlobalRoutingLSA::GlobalRoutingLSA (): Non-empty LSA in constructor"); + CopyLinkRecords (lsa); +} + + GlobalRoutingLSA& +GlobalRoutingLSA::operator= (const GlobalRoutingLSA& lsa) +{ + m_lsType = lsa.m_lsType; + m_linkStateId = lsa.m_linkStateId; + m_advertisingRtr = lsa.m_advertisingRtr; + m_networkLSANetworkMask = lsa.m_networkLSANetworkMask, + m_status = lsa.m_status; + + ClearLinkRecords (); + CopyLinkRecords (lsa); + return *this; +} + + void +GlobalRoutingLSA::CopyLinkRecords (const GlobalRoutingLSA& lsa) +{ + for (ListOfLinkRecords_t::const_iterator i = lsa.m_linkRecords.begin (); + i != lsa.m_linkRecords.end (); + i++) + { + GlobalRoutingLinkRecord *pSrc = *i; + GlobalRoutingLinkRecord *pDst = new GlobalRoutingLinkRecord; + + pDst->SetLinkType (pSrc->GetLinkType ()); + pDst->SetLinkId (pSrc->GetLinkId ()); + pDst->SetLinkData (pSrc->GetLinkData ()); + + m_linkRecords.push_back(pDst); + pDst = 0; + } + + m_attachedRouters = lsa.m_attachedRouters; +} + +GlobalRoutingLSA::~GlobalRoutingLSA() +{ + NS_DEBUG("GlobalRoutingLSA::~GlobalRoutingLSA ()"); + ClearLinkRecords (); +} + + void +GlobalRoutingLSA::ClearLinkRecords(void) +{ + for ( ListOfLinkRecords_t::iterator i = m_linkRecords.begin (); + i != m_linkRecords.end (); + i++) + { + NS_DEBUG("GlobalRoutingLSA::ClearLinkRecords (): free link record"); + + GlobalRoutingLinkRecord *p = *i; + delete p; + p = 0; + + *i = 0; + } + NS_DEBUG("GlobalRoutingLSA::ClearLinkRecords(): clear list"); + m_linkRecords.clear(); +} + + uint32_t +GlobalRoutingLSA::AddLinkRecord (GlobalRoutingLinkRecord* lr) +{ + m_linkRecords.push_back (lr); + return m_linkRecords.size (); +} + + uint32_t +GlobalRoutingLSA::GetNLinkRecords (void) const +{ + return m_linkRecords.size (); +} + + GlobalRoutingLinkRecord * +GlobalRoutingLSA::GetLinkRecord (uint32_t n) const +{ + uint32_t j = 0; + for ( ListOfLinkRecords_t::const_iterator i = m_linkRecords.begin (); + i != m_linkRecords.end (); + i++, j++) + { + if (j == n) + { + return *i; + } + } + NS_ASSERT_MSG(false, "GlobalRoutingLSA::GetLinkRecord (): invalid index"); + return 0; +} + + bool +GlobalRoutingLSA::IsEmpty (void) const +{ + return m_linkRecords.size () == 0; +} + + GlobalRoutingLSA::LSType +GlobalRoutingLSA::GetLSType (void) const +{ + return m_lsType; +} + + void +GlobalRoutingLSA::SetLSType (GlobalRoutingLSA::LSType typ) +{ + m_lsType = typ; +} + + Ipv4Address +GlobalRoutingLSA::GetLinkStateId (void) const +{ + return m_linkStateId; +} + + void +GlobalRoutingLSA::SetLinkStateId (Ipv4Address addr) +{ + m_linkStateId = addr; +} + + Ipv4Address +GlobalRoutingLSA::GetAdvertisingRouter (void) const +{ + return m_advertisingRtr; +} + + void +GlobalRoutingLSA::SetAdvertisingRouter (Ipv4Address addr) +{ + m_advertisingRtr = addr; +} + + void +GlobalRoutingLSA::SetNetworkLSANetworkMask (Ipv4Mask mask) +{ + m_networkLSANetworkMask = mask; +} + + Ipv4Mask +GlobalRoutingLSA::GetNetworkLSANetworkMask (void) const +{ + return m_networkLSANetworkMask; +} + + GlobalRoutingLSA::SPFStatus +GlobalRoutingLSA::GetStatus (void) const +{ + return m_status; +} + + uint32_t +GlobalRoutingLSA::AddAttachedRouter (Ipv4Address addr) +{ + m_attachedRouters.push_back (addr); + return m_attachedRouters.size (); +} + + uint32_t +GlobalRoutingLSA::GetNAttachedRouters (void) const +{ + return m_attachedRouters.size (); +} + + Ipv4Address +GlobalRoutingLSA::GetAttachedRouter (uint32_t n) const +{ + uint32_t j = 0; + for ( ListOfAttachedRouters_t::const_iterator i = m_attachedRouters.begin (); + i != m_attachedRouters.end (); + i++, j++) + { + if (j == n) + { + return *i; + } + } + NS_ASSERT_MSG(false, "GlobalRoutingLSA::GetAttachedRouter (): invalid index"); + return Ipv4Address("0.0.0.0"); +} + + void +GlobalRoutingLSA::SetStatus (GlobalRoutingLSA::SPFStatus status) +{ + m_status = status; +} + + void +GlobalRoutingLSA::Print (std::ostream &os) const +{ + os << "m_lsType = " << m_lsType << std::endl << + "m_linkStateId = " << m_linkStateId << std::endl << + "m_advertisingRtr = " << m_advertisingRtr << std::endl; + + if (m_lsType == GlobalRoutingLSA::RouterLSA) + { + for ( ListOfLinkRecords_t::const_iterator i = m_linkRecords.begin (); + i != m_linkRecords.end (); + i++) + { + GlobalRoutingLinkRecord *p = *i; + os << "----------" << std::endl; + os << "m_linkId = " << p->GetLinkId () << std::endl; + os << "m_linkData = " << p->GetLinkData () << std::endl; + } + } + else if (m_lsType == GlobalRoutingLSA::NetworkLSA) + { + os << "----------" << std::endl; + os << "m_networkLSANetworkMask = " << m_networkLSANetworkMask + << std::endl; + for ( ListOfAttachedRouters_t::const_iterator i = + m_attachedRouters.begin (); + i != m_attachedRouters.end (); + i++) + { + Ipv4Address p = *i; + os << "attachedRouter = " << p << std::endl; + } + } + else + { + NS_ASSERT_MSG(0, "Illegal LSA LSType: " << m_lsType); + } +} + +std::ostream& operator<< (std::ostream& os, GlobalRoutingLSA& lsa) +{ + lsa.Print (os); + return os; +} + +// --------------------------------------------------------------------------- +// +// GlobalRouter Implementation +// +// --------------------------------------------------------------------------- + +const InterfaceId GlobalRouter::iid = + MakeInterfaceId ("GlobalRouter", Object::iid); + +GlobalRouter::GlobalRouter (Ptr node) + : m_node(node), m_LSAs() +{ + NS_DEBUG("GlobalRouter::GlobalRouter ()"); + SetInterfaceId (GlobalRouter::iid); + m_routerId.Set(GlobalRouteManager::AllocateRouterId ()); +} + +GlobalRouter::~GlobalRouter () +{ + NS_DEBUG("GlobalRouter::~GlobalRouter ()"); + ClearLSAs(); +} + +void +GlobalRouter::DoDispose () +{ + m_node = 0; + Object::DoDispose (); +} + + void +GlobalRouter::ClearLSAs () +{ + NS_DEBUG("GlobalRouter::ClearLSAs ()"); + + for ( ListOfLSAs_t::iterator i = m_LSAs.begin (); + i != m_LSAs.end (); + i++) + { + NS_DEBUG("GlobalRouter::ClearLSAs (): free LSA"); + + GlobalRoutingLSA *p = *i; + delete p; + p = 0; + + *i = 0; + } + NS_DEBUG("GlobalRouter::ClearLSAs (): clear list"); + m_LSAs.clear(); +} + + Ipv4Address +GlobalRouter::GetRouterId (void) const +{ + return m_routerId; +} + +// +// Go out and discover any adjacent routers and build the Link State +// Advertisements that reflect them and their associated networks. +// + uint32_t +GlobalRouter::DiscoverLSAs (void) +{ + NS_DEBUG("GlobalRouter::DiscoverLSAs () for node " << m_node->GetId () ); + NS_ASSERT_MSG(m_node, + "GlobalRouter::DiscoverLSAs (): interface not set"); + + ClearLSAs (); + +// While building the router-LSA, keep a list of those NetDevices for +// which I am the designated router and need to later build a NetworkLSA + std::list > listOfDRInterfaces; + +// +// We're aggregated to a node. We need to ask the node for a pointer to its +// Ipv4 interface. This is where the information regarding the attached +// interfaces lives. +// + Ptr ipv4Local = m_node->QueryInterface (Ipv4::iid); + NS_ASSERT_MSG(ipv4Local, + "GlobalRouter::DiscoverLSAs (): QI for interface failed"); +// +// Each node originates a Router-LSA +// + GlobalRoutingLSA *pLSA = new GlobalRoutingLSA; + pLSA->SetLSType (GlobalRoutingLSA::RouterLSA); + pLSA->SetLinkStateId (m_routerId); + pLSA->SetAdvertisingRouter (m_routerId); + pLSA->SetStatus (GlobalRoutingLSA::LSA_SPF_NOT_EXPLORED); +// +// We need to ask the node for the number of net devices attached. This isn't +// necessarily equal to the number of links to adjacent nodes (other routers) +// as the number of devices may include those for stub networks (e.g., +// ethernets, etc.). +// + uint32_t numDevices = m_node->GetNDevices(); + NS_DEBUG("GlobalRouter::DiscoverLSAs (): numDevices = " << numDevices); + for (uint32_t i = 0; i < numDevices; ++i) + { + Ptr ndLocal = m_node->GetDevice(i); + + if (ndLocal->IsBroadcast () && !ndLocal->IsPointToPoint () ) + { + NS_DEBUG("GlobalRouter::DiscoverLSAs (): broadcast link"); + GlobalRoutingLinkRecord *plr = new GlobalRoutingLinkRecord; +// +// We need to determine whether we are on a transit or stub network +// If we find at least one more router on this channel, we are a transit +// +// +// Now, we have to find the Ipv4 interface whose netdevice is the one we +// just found. This is still the IP on the local side of the channel. There +// is a function to do this used down in the guts of the stack, but it's not +// exported so we had to whip up an equivalent. +// + uint32_t ifIndexLocal = FindIfIndexForDevice(m_node, ndLocal); + Ipv4Address addrLocal = ipv4Local->GetAddress(ifIndexLocal); + Ipv4Mask maskLocal = ipv4Local->GetNetworkMask(ifIndexLocal); + NS_DEBUG("Working with local address " << addrLocal); +// +// Now, we're going to walk over to the remote net device on the other end of +// the point-to-point channel we now know we have. This is where our adjacent +// router (to use OSPF lingo) is running. +// + Ptr ch = ndLocal->GetChannel(); + uint32_t nDevices = ch->GetNDevices(); + if (nDevices == 1) + { + // This is a stub broadcast interface + NS_DEBUG("GlobalRouter::DiscoverLSAs (): Router-LSA stub broadcast link"); + // XXX in future, need to consider if >1 includes other routers + plr->SetLinkType (GlobalRoutingLinkRecord::StubNetwork); + // Link ID is IP network number of attached network + plr->SetLinkId (addrLocal.CombineMask(maskLocal)); + // Link Data is network mask; convert to Ipv4Address + Ipv4Address maskLocalAddr; + maskLocalAddr.Set(maskLocal.GetHostOrder ()); + plr->SetLinkData (maskLocalAddr); + // Cost is interface's configured output cost (NOTYET) + plr->SetMetric (1); + pLSA->AddLinkRecord(plr); + plr = 0; + continue; + } + else + { + NS_DEBUG("GlobalRouter::DiscoverLSAs (): Router-LSA Broadcast link"); + // multiple routers on a broadcast interface + // lowest IP address is designated router + plr->SetLinkType (GlobalRoutingLinkRecord::TransitNetwork); + // Link ID is IP interface address of designated router + Ipv4Address desigRtr = + FindDesignatedRouterForLink (m_node, ndLocal); + if (desigRtr == addrLocal) + { + listOfDRInterfaces.push_back (ndLocal); + NS_DEBUG("GlobalRouter::DiscoverLSAs (): " << + m_node->GetId () << " is a DR"); + } + plr->SetLinkId (desigRtr); + // Link Data is router's own IP address + plr->SetLinkData (addrLocal); + // Cost is interface's configured output cost (NOTYET) + plr->SetMetric (1); + pLSA->AddLinkRecord (plr); + plr = 0; + continue; + } + } + else if (ndLocal->IsPointToPoint () ) + { + NS_DEBUG("GlobalRouter::DiscoverLSAs (): Router-LSA Point-to-point device"); +// +// Now, we have to find the Ipv4 interface whose netdevice is the one we +// just found. This is still the IP on the local side of the channel. There +// is a function to do this used down in the guts of the stack, but it's not +// exported so we had to whip up an equivalent. +// + uint32_t ifIndexLocal = FindIfIndexForDevice(m_node, ndLocal); +// +// Now that we have the Ipv4 interface index, we can get the address and mask +// we need. +// + Ipv4Address addrLocal = ipv4Local->GetAddress(ifIndexLocal); + Ipv4Mask maskLocal = ipv4Local->GetNetworkMask(ifIndexLocal); + NS_DEBUG("Working with local address " << addrLocal); +// +// Now, we're going to walk over to the remote net device on the other end of +// the point-to-point channel we now know we have. This is where our adjacent +// router (to use OSPF lingo) is running. +// + Ptr ch = ndLocal->GetChannel(); + Ptr ndRemote = GetAdjacent(ndLocal, ch); +// +// The adjacent net device is aggregated to a node. We need to ask that net +// device for its node, then ask that node for its Ipv4 interface. +// + Ptr nodeRemote = ndRemote->GetNode(); + Ptr ipv4Remote = nodeRemote->QueryInterface (Ipv4::iid); + NS_ASSERT_MSG(ipv4Remote, + "GlobalRouter::DiscoverLSAs (): QI for remote failed"); +// +// Per the OSPF spec, we're going to need the remote router ID, so we might as +// well get it now. +// + Ptr srRemote = + nodeRemote->QueryInterface (GlobalRouter::iid); + NS_ASSERT_MSG(srRemote, + "GlobalRouter::DiscoverLSAs():QI for remote failed"); + Ipv4Address rtrIdRemote = srRemote->GetRouterId(); + NS_DEBUG("Working with remote router " << rtrIdRemote); +// +// Now, just like we did above, we need to get the IP interface index for the +// net device on the other end of the point-to-point channel. +// + uint32_t ifIndexRemote = FindIfIndexForDevice(nodeRemote, ndRemote); +// +// Now that we have the Ipv4 interface, we can get the (remote) address and +// mask we need. +// + Ipv4Address addrRemote = ipv4Remote->GetAddress(ifIndexRemote); + Ipv4Mask maskRemote = ipv4Remote->GetNetworkMask(ifIndexRemote); + NS_DEBUG("Working with remote address " << addrRemote); +// +// Now we can fill out the link records for this link. There are always two +// link records; the first is a point-to-point record describing the link and +// the second is a stub network record with the network number. +// + GlobalRoutingLinkRecord *plr = new GlobalRoutingLinkRecord; + plr->SetLinkType (GlobalRoutingLinkRecord::PointToPoint); + plr->SetLinkId (rtrIdRemote); + plr->SetLinkData (addrLocal); + pLSA->AddLinkRecord (plr); + plr = 0; + + plr = new GlobalRoutingLinkRecord; + plr->SetLinkType (GlobalRoutingLinkRecord::StubNetwork); + plr->SetLinkId (addrRemote); + plr->SetLinkData (Ipv4Address(maskRemote.GetHostOrder())); // Frown + pLSA->AddLinkRecord (plr); + plr = 0; + } + else + { + NS_ASSERT_MSG(0, "GlobalRouter::DiscoverLSAs (): unknown link type"); + } + + } +// +// The LSA goes on a list of LSAs in case we want to begin exporting other +// kinds of advertisements (than Router LSAs). + m_LSAs.push_back (pLSA); + NS_DEBUG(*pLSA); + + +// Now, determine whether we need to build a NetworkLSA + if (listOfDRInterfaces.size () > 0) + { + for (std::list >::iterator i = listOfDRInterfaces.begin (); + i != listOfDRInterfaces.end (); i++) + { +// Build one NetworkLSA for each interface that is a DR + Ptr ndLocal = *i; + uint32_t ifIndexLocal = FindIfIndexForDevice(m_node, ndLocal); + Ipv4Address addrLocal = ipv4Local->GetAddress(ifIndexLocal); + Ipv4Mask maskLocal = ipv4Local->GetNetworkMask(ifIndexLocal); + + GlobalRoutingLSA *pLSA = new GlobalRoutingLSA; + pLSA->SetLSType (GlobalRoutingLSA::NetworkLSA); + pLSA->SetLinkStateId (addrLocal); + pLSA->SetAdvertisingRouter (m_routerId); + pLSA->SetNetworkLSANetworkMask (maskLocal); + pLSA->SetStatus (GlobalRoutingLSA::LSA_SPF_NOT_EXPLORED); +// Build list of AttachedRouters + Ptr ch = ndLocal->GetChannel(); + uint32_t nDevices = ch->GetNDevices(); + NS_ASSERT (nDevices); + for (uint32_t i = 0; i < nDevices; i++) + { + Ptr tempNd = ch->GetDevice (i); + NS_ASSERT (tempNd); + Ptr tempNode = tempNd->GetNode (); + uint32_t tempIfIndex = FindIfIndexForDevice (tempNode, tempNd); + Ptr tempIpv4 = tempNode->QueryInterface (Ipv4::iid); + NS_ASSERT (tempIpv4); + Ipv4Address tempAddr = tempIpv4->GetAddress(tempIfIndex); + pLSA->AddAttachedRouter (tempAddr); + } + m_LSAs.push_back (pLSA); + NS_DEBUG(*pLSA); + } + } + + return m_LSAs.size (); +} + + Ipv4Address +GlobalRouter::FindDesignatedRouterForLink (Ptr node, + Ptr ndLocal) const +{ + uint32_t ifIndexLocal = FindIfIndexForDevice(node, ndLocal); + Ptr ipv4Local = m_node->QueryInterface (Ipv4::iid); + NS_ASSERT (ipv4Local); + Ipv4Address addrLocal = ipv4Local->GetAddress(ifIndexLocal); + Ipv4Mask maskLocal = ipv4Local->GetNetworkMask(ifIndexLocal); + + Ptr ch = ndLocal->GetChannel(); + uint32_t nDevices = ch->GetNDevices(); + NS_ASSERT (nDevices); + Ipv4Address lowest = addrLocal; + // iterate all NetDevices and return the lowest numbered IP address + for (uint32_t i = 0; i < nDevices; i++) + { + Ptr tempNd = ch->GetDevice (i); + NS_ASSERT (tempNd); + Ptr tempNode = tempNd->GetNode (); + uint32_t tempIfIndex = FindIfIndexForDevice (tempNode, tempNd); + Ptr tempIpv4 = tempNode->QueryInterface (Ipv4::iid); + NS_ASSERT (tempIpv4); + Ipv4Address tempAddr = tempIpv4->GetAddress(tempIfIndex); + if (tempAddr < addrLocal) + { + addrLocal = tempAddr; + } + } + return addrLocal; +} + + uint32_t +GlobalRouter::GetNumLSAs (void) const +{ + NS_DEBUG("GlobalRouter::GetNumLSAs ()"); + return m_LSAs.size (); +} + +// +// Get the nth link state advertisement from this router. +// + bool +GlobalRouter::GetLSA (uint32_t n, GlobalRoutingLSA &lsa) const +{ + NS_ASSERT_MSG(lsa.IsEmpty(), "GlobalRouter::GetLSA (): Must pass empty LSA"); +// +// All of the work was done in GetNumLSAs. All we have to do here is to +// walk the list of link state advertisements created there and return the +// one the client is interested in. +// + ListOfLSAs_t::const_iterator i = m_LSAs.begin (); + uint32_t j = 0; + + for (; i != m_LSAs.end (); i++, j++) + { + if (j == n) + { + GlobalRoutingLSA *p = *i; + lsa = *p; + return true; + } + } + + return false; +} + +// +// Link through the given channel and find the net device that's on the +// other end. This only makes sense with a point-to-point channel. +// + Ptr +GlobalRouter::GetAdjacent(Ptr nd, Ptr ch) const +{ + + NS_ASSERT_MSG(ch->GetNDevices() == 2, + "GlobalRouter::GetAdjacent (): Channel with other than two devices"); +// +// This is a point to point channel with two endpoints. Get both of them. +// + Ptr nd1 = ch->GetDevice(0); + Ptr nd2 = ch->GetDevice(1); +// +// One of the endpoints is going to be "us" -- that is the net device attached +// to the node on which we're running -- i.e., "nd". The other endpoint (the +// one to which we are connected via the channel) is the adjacent router. +// + if (nd1 == nd) + { + return nd2; + } + else if (nd2 == nd) + { + return nd1; + } + else + { + NS_ASSERT_MSG(false, + "GlobalRouter::GetAdjacent (): Wrong or confused channel?"); + return 0; + } +} + +// +// Given a node and a net device, find the IPV4 interface index that +// corresponds to that net device. +// + uint32_t +GlobalRouter::FindIfIndexForDevice(Ptr node, Ptr nd) const +{ + Ptr ipv4 = node->QueryInterface (Ipv4::iid); + NS_ASSERT_MSG(ipv4, "QI for interface failed"); + for (uint32_t i = 0; i < ipv4->GetNInterfaces(); ++i ) + { + if (ipv4->GetNetDevice(i) == nd) + { + return i; + } + } + + NS_ASSERT_MSG(0, "Cannot find interface for device"); + return 0; +} + +} // namespace ns3 diff --git a/src/routing/global-routing/global-router-interface.h b/src/routing/global-routing/global-router-interface.h new file mode 100644 index 000000000..b6558eb20 --- /dev/null +++ b/src/routing/global-routing/global-router-interface.h @@ -0,0 +1,667 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#ifndef GLOBAL_ROUTER_INTERFACE_H +#define GLOBAL_ROUTER_INTERFACE_H + +#include +#include +#include "ns3/object.h" +#include "ns3/ptr.h" +#include "ns3/node.h" +#include "ns3/channel.h" +#include "ns3/ipv4-address.h" +#include "ns3/global-route-manager.h" + +namespace ns3 { + +/** + * @brief A single link record for a link state advertisement. + * + * The GlobalRoutingLinkRecord is modeled after the OSPF link record field of + * a Link State Advertisement. Right now we will only see two types of link + * records corresponding to a stub network and a point-to-point link (channel). + */ +class GlobalRoutingLinkRecord +{ +public: +/** + * @enum LinkType + * @brief Enumeration of the possible types of Global Routing Link Records. + * + * These values are defined in the OSPF spec. We currently only use + * PointToPoint and StubNetwork types. + */ + enum LinkType { + Unknown = 0, /**< Uninitialized Link Record */ + PointToPoint, /**< Record representing a point to point channel */ + TransitNetwork, /**< Unused -- for future OSPF compatibility */ + StubNetwork, /**< Record represents a leaf node network */ + VirtualLink /**< Unused -- for future OSPF compatibility */ + }; + +/** + * @brief Construct an empty ("uninitialized") Global Routing Link Record. + * + * The Link ID and Link Data Ipv4 addresses are set to "0.0.0.0"; + * The Link Type is set to Unknown; + * The metric is set to 0. + */ + GlobalRoutingLinkRecord (); + +/** + * Construct an initialized Global Routing Link Record. + * + * @param linkType The type of link record to construct. + * @param linkId The link ID for the record. + * @param linkData The link data field for the record. + * @param metric The metric field for the record. + * @see LinkType + * @see SetLinkId + * @see SetLinkData + */ + GlobalRoutingLinkRecord ( + LinkType linkType, + Ipv4Address linkId, + Ipv4Address linkData, + uint32_t metric); + +/** + * @brief Destroy a Global Routing Link Record. + * + * Currently does nothing. Here as a placeholder only. + */ + ~GlobalRoutingLinkRecord (); + +/** + * Get the Link ID field of the Global Routing Link Record. + * + * For an OSPF type 1 link (PointToPoint) the Link ID will be the Router ID + * of the neighboring router. + * + * For an OSPF type 3 link (StubNetwork), the Link ID will be the adjacent + * neighbor's IP address + * + * @returns The Ipv4Address corresponding to the Link ID field of the record. + */ + Ipv4Address GetLinkId(void) const; + +/** + * @brief Set the Link ID field of the Global Routing Link Record. + * + * For an OSPF type 1 link (PointToPoint) the Link ID must be the Router ID + * of the neighboring router. + * + * For an OSPF type 3 link (StubNetwork), the Link ID must be the adjacent + * neighbor's IP address + * + * @param addr An Ipv4Address to store in the Link ID field of the record. + */ + void SetLinkId(Ipv4Address addr); + +/** + * @brief Get the Link Data field of the Global Routing Link Record. + * + * For an OSPF type 1 link (PointToPoint) the Link Data will be the IP + * address of the node of the local side of the link. + * + * For an OSPF type 3 link (StubNetwork), the Link Data will be the + * network mask + * + * @returns The Ipv4Address corresponding to the Link Data field of the record. + */ + Ipv4Address GetLinkData(void) const; + +/** + * @brief Set the Link Data field of the Global Routing Link Record. + * + * For an OSPF type 1 link (PointToPoint) the Link Data must be the IP + * address of the node of the local side of the link. + * + * For an OSPF type 3 link (StubNetwork), the Link Data must be set to the + * network mask + * + * @param addr An Ipv4Address to store in the Link Data field of the record. + */ + void SetLinkData(Ipv4Address addr); + +/** + * @brief Get the Link Type field of the Global Routing Link Record. + * + * The Link Type describes the kind of link a given record represents. The + * values are defined by OSPF. + * + * @see LinkType + * @returns The LinkType of the current Global Routing Link Record. + */ + LinkType GetLinkType(void) const; + +/** + * @brief Set the Link Type field of the Global Routing Link Record. + * + * The Link Type describes the kind of link a given record represents. The + * values are defined by OSPF. + * + * @see LinkType + * @param linkType The new LinkType for the current Global Routing Link Record. + */ + void SetLinkType(LinkType linkType); + +/** + * @brief Get the Metric Data field of the Global Routing Link Record. + * + * The metric is an abstract cost associated with forwarding a packet across + * a link. A sum of metrics must have a well-defined meaning. That is, you + * shouldn't use bandwidth as a metric (how does the sum of the bandwidth of + * two hops relate to the cost of sending a packet); rather you should use + * something like delay. + * + * @returns The metric field of the Global Routing Link Record. + */ + uint32_t GetMetric(void) const; + +/** + * @brief Set the Metric Data field of the Global Routing Link Record. + * + * The metric is an abstract cost associated with forwarding a packet across + * a link. A sum of metrics must have a well-defined meaning. That is, you + * shouldn't use bandwidth as a metric (how does the sum of the bandwidth of + * two hops relate to the cost of sending a packet); rather you should use + * something like delay. + * + * @param metric The new metric for the current Global Routing Link Record. + */ + void SetMetric(uint32_t metric); + +private: +/** + * m_linkId and m_linkData are defined by OSPF to have different meanings + * depending on the type of link a given link records represents. They work + * together. + * + * For Type 1 link (PointToPoint), set m_linkId to Router ID of + * neighboring router. + * + * For Type 3 link (Stub), set m_linkId to neighbor's IP address + */ + Ipv4Address m_linkId; + +/** + * m_linkId and m_linkData are defined by OSPF to have different meanings + * depending on the type of link a given link records represents. They work + * together. + * + * For Type 1 link (PointToPoint), set m_linkData to local IP address + * + * For Type 3 link (Stub), set m_linkData to mask + */ + Ipv4Address m_linkData; // for links to RouterLSA, + +/** + * The type of the Global Routing Link Record. Defined in the OSPF spec. + * We currently only use PointToPoint and StubNetwork types. + */ + LinkType m_linkType; + +/** + * The metric for a given link. + * + * A metric is abstract cost associated with forwarding a packet across a + * link. A sum of metrics must have a well-defined meaning. That is, you + * shouldn't use bandwidth as a metric (how does the sum of the bandwidth + * of two hops relate to the cost of sending a packet); rather you should + * use something like delay. + */ + uint32_t m_metric; +}; + +/** + * @brief a Link State Advertisement (LSA) for a router, used in global + * routing. + * + * Roughly equivalent to a global incarnation of the OSPF link state header + * combined with a list of Link Records. Since it's global, there's + * no need for age or sequence number. See RFC 2328, Appendix A. + */ +class GlobalRoutingLSA +{ +public: +/** + * @enum LSType + * @brief corresponds to LS type field of RFC 2328 OSPF LSA header + */ + enum LSType { + Unknown = 0, /**< Uninitialized Type */ + RouterLSA, + NetworkLSA, + SummaryLSA, + SummaryLSA_ASBR, + ASExternalLSAs + }; +/** + * @enum SPFStatus + * @brief Enumeration of the possible values of the status flag in the Routing + * Link State Advertisements. + */ + enum SPFStatus { + LSA_SPF_NOT_EXPLORED = 0, /**< New vertex not yet considered */ + LSA_SPF_CANDIDATE, /**< Vertex is in the SPF candidate queue */ + LSA_SPF_IN_SPFTREE /**< Vertex is in the SPF tree */ + }; +/** + * @brief Create a blank Global Routing Link State Advertisement. + * + * On completion Ipv4Address variables initialized to 0.0.0.0 and the + * list of Link State Records is empty. + */ + GlobalRoutingLSA(); + +/** + * @brief Create an initialized Global Routing Link State Advertisement. + * + * On completion the list of Link State Records is empty. + * + * @param status The status to of the new LSA. + * @param linkStateId The Ipv4Address for the link state ID field. + * @param advertisingRtr The Ipv4Address for the advertising router field. + */ + GlobalRoutingLSA(SPFStatus status, Ipv4Address linkStateId, + Ipv4Address advertisingRtr); + +/** + * @brief Copy constructor for a Global Routing Link State Advertisement. + * + * Takes a piece of memory and constructs a semantically identical copy of + * the given LSA. + * + * @param lsa The existing LSA to be used as the source. + */ + GlobalRoutingLSA (GlobalRoutingLSA& lsa); + +/** + * @brief Destroy an existing Global Routing Link State Advertisement. + * + * Any Global Routing Link Records present in the list are freed. + */ + ~GlobalRoutingLSA(); + +/** + * @brief Assignment operator for a Global Routing Link State Advertisement. + * + * Takes an existing Global Routing Link State Advertisement and overwrites + * it to make a semantically identical copy of a given prototype LSA. + * + * If there are any Global Routing Link Records present in the existing + * LSA, they are freed before the assignment happens. + * + * @param lsa The existing LSA to be used as the source. + * @returns Reference to the overwritten LSA. + */ + GlobalRoutingLSA& operator= (const GlobalRoutingLSA& lsa); + +/** + * @brief Copy any Global Routing Link Records in a given Global Routing Link + * State Advertisement to the current LSA. + * + * Existing Link Records are not deleted -- this is a concatenation of Link + * Records. + * + * @see ClearLinkRecords () + * @param lsa The LSA to copy the Link Records from. + */ + void CopyLinkRecords (const GlobalRoutingLSA& lsa); + +/** + * @brief Add a given Global Routing Link Record to the LSA. + * + * @param lr The Global Routing Link Record to be added. + * @returns The number of link records in the list. + */ + uint32_t AddLinkRecord (GlobalRoutingLinkRecord* lr); + +/** + * @brief Return the number of Global Routing Link Records in the LSA. + * + * @returns The number of link records in the list. + */ + uint32_t GetNLinkRecords (void) const; + +/** + * @brief Return a pointer to the specified Global Routing Link Record. + * + * @param n The LSA number desired. + * @returns The number of link records in the list. + */ + GlobalRoutingLinkRecord* GetLinkRecord (uint32_t n) const; + +/** + * @brief Release all of the Global Routing Link Records present in the Global + * Routing Link State Advertisement and make the list of link records empty. + */ + void ClearLinkRecords(void); + +/** + * @brief Check to see if the list of Global Routing Link Records present in the + * Global Routing Link State Advertisement is empty. + * + * @returns True if the list is empty, false otherwise. + */ + bool IsEmpty(void) const; + +/** + * @brief Print the contents of the Global Routing Link State Advertisement and + * any Global Routing Link Records present in the list. Quite verbose. + */ + void Print (std::ostream &os) const; + +/** + * @brief Return the LSType field of the LSA + */ + LSType GetLSType (void) const; +/** + * @brief Set the LS type field of the LSA + */ + void SetLSType (LSType typ); + +/** + * @brief Get the Link State ID as defined by the OSPF spec. We always set it + * to the router ID of the router making the advertisement. + * + * @see RoutingEnvironment::AllocateRouterId () + * @see GlobalRouting::GetRouterId () + * @returns The Ipv4Address stored as the link state ID. + */ + Ipv4Address GetLinkStateId (void) const; + +/** + * @brief Set the Link State ID is defined by the OSPF spec. We always set it + * to the router ID of the router making the advertisement. + * + * @see RoutingEnvironment::AllocateRouterId () + * @see GlobalRouting::GetRouterId () + */ + void SetLinkStateId (Ipv4Address addr); + +/** + * @brief Get the Advertising Router as defined by the OSPF spec. We always + * set it to the router ID of the router making the advertisement. + * + * @see RoutingEnvironment::AllocateRouterId () + * @see GlobalRouting::GetRouterId () + * @returns The Ipv4Address stored as the advetising router. + */ + Ipv4Address GetAdvertisingRouter (void) const; + +/** + * @brief Set the Advertising Router as defined by the OSPF spec. We always + * set it to the router ID of the router making the advertisement. + * + * @see RoutingEnvironment::AllocateRouterId () + * @see GlobalRouting::GetRouterId () + */ + void SetAdvertisingRouter (Ipv4Address rtr); + +/** + * @brief For a Network LSA, set the Network Mask field that precedes + * the list of attached routers. + */ + void SetNetworkLSANetworkMask (Ipv4Mask mask); + +/** + * @brief For a Network LSA, get the Network Mask field that precedes + * the list of attached routers. + * + * @returns the NetworkLSANetworkMask + */ + Ipv4Mask GetNetworkLSANetworkMask (void) const; + +/** + * @brief Add an attached router to the list in the NetworkLSA + * + * @param addr The Ipv4Address of the interface on the network link + * @returns The number of addresses in the list. + */ + uint32_t AddAttachedRouter (Ipv4Address addr); + +/** + * @brief Return the number of attached routers listed in the NetworkLSA + * + * @returns The number of attached routers. + */ + uint32_t GetNAttachedRouters (void) const; + +/** + * @brief Return an Ipv4Address corresponding to the specified attached router + * + * @param n The attached router number desired (number in the list). + * @returns The Ipv4Address of the requested router + */ + Ipv4Address GetAttachedRouter (uint32_t n) const; + +/** + * @brief Get the SPF status of the advertisement. + * + * @see SPFStatus + * @returns The SPFStatus of the LSA. + */ + SPFStatus GetStatus (void) const; + +/** + * @brief Set the SPF status of the advertisement + * + * @see SPFStatus + */ + void SetStatus (SPFStatus status); + +private: +/** + * The type of the LSA. Each LSA type has a separate advertisement + * format. + */ + LSType m_lsType; +/** + * The Link State ID is defined by the OSPF spec. We always set it to the + * router ID of the router making the advertisement. + * + * @see RoutingEnvironment::AllocateRouterId () + * @see GlobalRouting::GetRouterId () + */ + Ipv4Address m_linkStateId; + +/** + * The Advertising Router is defined by the OSPF spec. We always set it to + * the router ID of the router making the advertisement. + * + * @see RoutingEnvironment::AllocateRouterId () + * @see GlobalRouting::GetRouterId () + */ + Ipv4Address m_advertisingRtr; + +/** + * A convenience typedef to avoid too much writers cramp. + */ + typedef std::list ListOfLinkRecords_t; + +/** + * Each Link State Advertisement contains a number of Link Records that + * describe the kinds of links that are attached to a given node. We + * consider PointToPoint and StubNetwork links. + * + * m_linkRecords is an STL list container to hold the Link Records that have + * been discovered and prepared for the advertisement. + * + * @see GlobalRouting::DiscoverLSAs () + */ + ListOfLinkRecords_t m_linkRecords; + +/** + * Each Network LSA contains the network mask of the attached network + */ + Ipv4Mask m_networkLSANetworkMask; + +/** + * A convenience typedef to avoid too much writers cramp. + */ + typedef std::list ListOfAttachedRouters_t; + +/** + * Each Network LSA contains a list of attached routers + * + * m_attachedRouters is an STL list container to hold the addresses that have + * been discovered and prepared for the advertisement. + * + * @see GlobalRouting::DiscoverLSAs () + */ + ListOfAttachedRouters_t m_attachedRouters; + +/** + * This is a tristate flag used internally in the SPF computation to mark + * if an SPFVertex (a data structure representing a vertex in the SPF tree + * -- a router) is new, is a candidate for a shortest path, or is in its + * proper position in the tree. + */ + SPFStatus m_status; +}; + +std::ostream& operator<< (std::ostream& os, GlobalRoutingLSA& lsa); + +/** + * @brief An interface aggregated to a node to provide global routing info + * + * An interface aggregated to a node that provides global routing information + * to a global route manager. The presence of the interface indicates that + * the node is a router. The interface is the mechanism by which the router + * advertises its connections to neighboring routers. We're basically + * allowing the route manager to query for link state advertisements. + */ +class GlobalRouter : public Object +{ +public: +/** + * @brief The Interface ID of the Global Router interface. + * + * @see Object::QueryInterface () + */ + static const InterfaceId iid; + +/** + * @brief Create a Global Router class and aggregate its interface onto the + * Node provided. + * + * @param node The existing Node onto which this router will be aggregated. + */ + GlobalRouter (Ptr node); + +/** + * @brief Get the Router ID associated with this Global Router. + * + * The Router IDs are allocated in the RoutingEnvironment -- one per Router, + * starting at 0.0.0.1 and incrementing with each instantiation of a router. + * + * @see RoutingEnvironment::AllocateRouterId () + * @returns The Router ID associated with the Global Router. + */ + Ipv4Address GetRouterId (void) const; + +/** + * @brief Walk the connected channels, discover the adjacent routers and build + * the associated number of Global Routing Link State Advertisements that + * this router can export. + * + * This is a fairly expensive operation in that every time it is called + * the current list of LSAs is built by walking connected point-to-point + * channels and peeking into adjacent IPV4 stacks to get address information. + * This is done to allow for limited dymanics of the Global Routing + * environment. By that we mean that you can discover new link state + * advertisements after a network topology change by calling DiscoverLSAs + * and then by reading those advertisements. + * + * @see GlobalRoutingLSA + * @see GlobalRouter::GetLSA () + * @returns The number of Global Routing Link State Advertisements. + */ + uint32_t DiscoverLSAs (void); + +/** + * @brief Get the Number of Global Routing Link State Advertisements that this + * router can export. + * + * To get meaningful information you must have previously called DiscoverLSAs. + * After you know how many LSAs are present in the router, you may call + * GetLSA () to retrieve the actual advertisement. + * + * @see GlobalRouterLSA + * @see GlobalRouting::DiscoverLSAs () + * @see GlobalRouting::GetLSA () + * @returns The number of Global Routing Link State Advertisements. + */ + uint32_t GetNumLSAs (void) const; + +/** + * @brief Get a Global Routing Link State Advertisements that this router has + * said that it can export. + * + * This is a fairly inexpensive expensive operation in that the hard work + * was done in GetNumLSAs. We just copy the indicated Global Routing Link + * State Advertisement into the requested GlobalRoutingLSA object. + * + * You must call GlobalRouter::GetNumLSAs before calling this method in + * order to discover the adjacent routers and build the advertisements. + * GetNumLSAs will return the number of LSAs this router advertises. + * The parameter n (requested LSA number) must be in the range 0 to + * GetNumLSAs() - 1. + * + * @see GlobalRoutingLSA + * @see GlobalRouting::GetNumLSAs () + * @param n The index number of the LSA you want to read. + * @param lsa The GlobalRoutingLSA class to receive the LSA information. + * @returns The number of Global Router Link State Advertisements. + */ + bool GetLSA (uint32_t n, GlobalRoutingLSA &lsa) const; + +private: + virtual ~GlobalRouter (); + void ClearLSAs (void); + + Ptr GetAdjacent(Ptr nd, Ptr ch) const; + uint32_t FindIfIndexForDevice(Ptr node, Ptr nd) const; + Ipv4Address FindDesignatedRouterForLink (Ptr node, + Ptr ndLocal) const; + + Ptr m_node; + + typedef std::list ListOfLSAs_t; + ListOfLSAs_t m_LSAs; + + Ipv4Address m_routerId; + + // inherited from Object + virtual void DoDispose (void); + +/** + * @brief Global Router copy construction is disallowed. + */ + GlobalRouter (GlobalRouter& sr); + +/** + * @brief Global Router assignment operator is disallowed. + */ + GlobalRouter& operator= (GlobalRouter& sr); +}; + +} // namespace ns3 + +#endif /* GLOBAL_ROUTER_INTERFACE_H */ diff --git a/src/routing/global-routing/wscript b/src/routing/global-routing/wscript new file mode 100644 index 000000000..b14a5c502 --- /dev/null +++ b/src/routing/global-routing/wscript @@ -0,0 +1,17 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +def build(bld): + module = bld.create_ns3_module('global-routing', ['node']) + module.source = [ + 'global-router-interface.cc', + 'global-route-manager.cc', + 'global-route-manager-impl.cc', + 'candidate-queue.cc', + ] + headers = bld.create_obj('ns3header') + headers.source = [ + 'global-router-interface.h', + 'global-route-manager.h', + 'candidate-queue.h', + ] + diff --git a/src/simulator/event-id.cc b/src/simulator/event-id.cc index 1c2915037..ca3766503 100644 --- a/src/simulator/event-id.cc +++ b/src/simulator/event-id.cc @@ -30,7 +30,7 @@ EventId::EventId () m_uid (0) {} -EventId::EventId (EventImpl *impl, uint64_t ts, uint32_t uid) +EventId::EventId (const Ptr &impl, uint64_t ts, uint32_t uid) : m_eventImpl (impl), m_ts (ts), m_uid (uid) @@ -38,11 +38,7 @@ EventId::EventId (EventImpl *impl, uint64_t ts, uint32_t uid) void EventId::Cancel (void) { - if (!IsExpired ()) - { - m_eventImpl->Cancel (); - m_eventImpl = 0; - } + Simulator::Cancel (*this); } bool EventId::IsExpired (void) const @@ -55,9 +51,9 @@ EventId::IsRunning (void) const return !IsExpired (); } EventImpl * -EventId::GetEventImpl (void) const +EventId::PeekEventImpl (void) const { - return m_eventImpl; + return PeekPointer (m_eventImpl); } uint64_t EventId::GetTs (void) const diff --git a/src/simulator/event-id.h b/src/simulator/event-id.h index 52371faa1..efa74d930 100644 --- a/src/simulator/event-id.h +++ b/src/simulator/event-id.h @@ -22,6 +22,8 @@ #define EVENT_ID_H #include +#include "ns3/ptr.h" +#include "event-impl.h" namespace ns3 { @@ -33,7 +35,7 @@ class EventImpl; class EventId { public: EventId (); - EventId (EventImpl *impl, uint64_t ts, uint32_t uid); + EventId (const Ptr &impl, uint64_t ts, uint32_t uid); /** * This method is syntactic sugar for the ns3::Simulator::cancel * method. @@ -51,12 +53,12 @@ public: * they are supposed to be invoked only by * subclasses of the Scheduler base class. */ - EventImpl *GetEventImpl (void) const; + EventImpl *PeekEventImpl (void) const; uint64_t GetTs (void) const; uint32_t GetUid (void) const; private: friend bool operator == (const EventId &a, const EventId &b); - EventImpl *m_eventImpl; + Ptr m_eventImpl; uint64_t m_ts; uint32_t m_uid; }; diff --git a/src/simulator/event-impl.cc b/src/simulator/event-impl.cc index fb6c7b1ad..9c89bb52f 100644 --- a/src/simulator/event-impl.cc +++ b/src/simulator/event-impl.cc @@ -29,7 +29,8 @@ EventImpl::~EventImpl () {} EventImpl::EventImpl () - : m_cancel (false) + : m_cancel (false), + m_count (1) {} void EventImpl::Invoke (void) diff --git a/src/simulator/event-impl.h b/src/simulator/event-impl.h index fd7d54e2d..51ac1e7a1 100644 --- a/src/simulator/event-impl.h +++ b/src/simulator/event-impl.h @@ -28,6 +28,8 @@ namespace ns3 { class EventImpl { public: EventImpl (); + inline void Ref (void) const; + inline void Unref (void) const; virtual ~EventImpl () = 0; void Invoke (void); void Cancel (void); @@ -37,8 +39,28 @@ protected: private: friend class Event; bool m_cancel; + mutable uint32_t m_count; }; }; // namespace ns3 +namespace ns3 { + +void +EventImpl::Ref (void) const +{ + m_count++; +} +void +EventImpl::Unref (void) const +{ + m_count--; + if (m_count == 0) + { + delete this; + } +} + +} // namespace ns3 + #endif /* EVENT_IMPL_H */ diff --git a/src/simulator/high-precision-128.h b/src/simulator/high-precision-128.h index 8ede96ec8..775f59ea0 100644 --- a/src/simulator/high-precision-128.h +++ b/src/simulator/high-precision-128.h @@ -222,7 +222,9 @@ HighPrecision::Compare (HighPrecision const &o) const HP128INC (m_nslowcmps); return SlowCompare (o); } - + // The below statement is unreachable but necessary for optimized + // builds with gcc-4.0.x due to a compiler bug. + return 0; } HighPrecision HighPrecision::Zero (void) diff --git a/src/simulator/scheduler-heap.cc b/src/simulator/scheduler-heap.cc index 0339dfed1..f924c70f4 100644 --- a/src/simulator/scheduler-heap.cc +++ b/src/simulator/scheduler-heap.cc @@ -170,7 +170,7 @@ SchedulerHeap::Smallest (uint32_t a, uint32_t b) const } bool -SchedulerHeap::RealIsEmpty (void) const +SchedulerHeap::IsEmpty (void) const { return (m_heap.size () == 1)?true:false; } @@ -223,9 +223,11 @@ SchedulerHeap::TopDown (uint32_t start) void -SchedulerHeap::RealInsert (EventId id) +SchedulerHeap::Insert (const EventId &id) { - EventImpl *event = id.GetEventImpl (); + // acquire single ref + EventImpl *event = id.PeekEventImpl (); + event->Ref (); Scheduler::EventKey key; key.m_ts = id.GetTs (); key.m_uid = id.GetUid (); @@ -234,29 +236,34 @@ SchedulerHeap::RealInsert (EventId id) } EventId -SchedulerHeap::RealPeekNext (void) const +SchedulerHeap::PeekNext (void) const { std::pair next = m_heap[Root ()]; return EventId (next.first, next.second.m_ts, next.second.m_uid); } -void -SchedulerHeap::RealRemoveNext (void) +EventId +SchedulerHeap::RemoveNext (void) { + std::pair next = m_heap[Root ()]; Exch (Root (), Last ()); m_heap.pop_back (); TopDown (Root ()); + return EventId (Ptr (next.first, false), next.second.m_ts, next.second.m_uid); } bool -SchedulerHeap::RealRemove (EventId id) +SchedulerHeap::Remove (const EventId &id) { uint32_t uid = id.GetUid (); for (uint32_t i = 1; i < m_heap.size (); i++) { if (uid == m_heap[i].second.m_uid) { - NS_ASSERT (m_heap[i].first == id.GetEventImpl ()); + NS_ASSERT (m_heap[i].first == id.PeekEventImpl ()); + std::pair next = m_heap[i]; + // release single ref + next.first->Unref (); Exch (i, Last ()); m_heap.pop_back (); TopDown (i); diff --git a/src/simulator/scheduler-heap.h b/src/simulator/scheduler-heap.h index c57fe1368..fbc6e1ca1 100644 --- a/src/simulator/scheduler-heap.h +++ b/src/simulator/scheduler-heap.h @@ -35,13 +35,13 @@ public: SchedulerHeap (); virtual ~SchedulerHeap (); -private: - virtual void RealInsert (EventId id); - virtual bool RealIsEmpty (void) const; - virtual EventId RealPeekNext (void) const; - virtual void RealRemoveNext (void); - virtual bool RealRemove (EventId ev); + virtual void Insert (const EventId &id); + virtual bool IsEmpty (void) const; + virtual EventId PeekNext (void) const; + virtual EventId RemoveNext (void); + virtual bool Remove (const EventId &ev); +private: typedef std::vector > BinaryHeap; inline uint32_t Parent (uint32_t id) const; diff --git a/src/simulator/scheduler-list.cc b/src/simulator/scheduler-list.cc index ba3960458..c3de7891a 100644 --- a/src/simulator/scheduler-list.cc +++ b/src/simulator/scheduler-list.cc @@ -67,10 +67,12 @@ SchedulerList::IsLower (Scheduler::EventKey const*a, Scheduler::EventKey const*b } void -SchedulerList::RealInsert (EventId id) +SchedulerList::Insert (const EventId &id) { Scheduler::EventKey key; - EventImpl *event = id.GetEventImpl (); + // acquire refcount on EventImpl + EventImpl *event = id.PeekEventImpl (); + event->Ref (); key.m_ts = id.GetTs (); key.m_uid = id.GetUid (); for (EventsI i = m_events.begin (); i != m_events.end (); i++) @@ -84,31 +86,35 @@ SchedulerList::RealInsert (EventId id) m_events.push_back (std::make_pair (event, key)); } bool -SchedulerList::RealIsEmpty (void) const +SchedulerList::IsEmpty (void) const { return m_events.empty (); } EventId -SchedulerList::RealPeekNext (void) const +SchedulerList::PeekNext (void) const { std::pair next = m_events.front (); return EventId (next.first, next.second.m_ts, next.second.m_uid); } -void -SchedulerList::RealRemoveNext (void) +EventId +SchedulerList::RemoveNext (void) { + std::pair next = m_events.front (); m_events.pop_front (); + return EventId (Ptr (next.first,false), next.second.m_ts, next.second.m_uid); } bool -SchedulerList::RealRemove (EventId id) +SchedulerList::Remove (const EventId &id) { for (EventsI i = m_events.begin (); i != m_events.end (); i++) { if (i->second.m_uid == id.GetUid ()) { - NS_ASSERT (id.GetEventImpl () == i->first); + NS_ASSERT (id.PeekEventImpl () == i->first); + // release single acquire ref. + i->first->Unref (); m_events.erase (i); return true; } diff --git a/src/simulator/scheduler-list.h b/src/simulator/scheduler-list.h index cbce5e68f..306d19270 100644 --- a/src/simulator/scheduler-list.h +++ b/src/simulator/scheduler-list.h @@ -37,13 +37,13 @@ class SchedulerList : public Scheduler { SchedulerList (); virtual ~SchedulerList (); - private: - virtual void RealInsert (EventId id); - virtual bool RealIsEmpty (void) const; - virtual EventId RealPeekNext (void) const; - virtual void RealRemoveNext (void); - virtual bool RealRemove (EventId ev); + virtual void Insert (const EventId &id); + virtual bool IsEmpty (void) const; + virtual EventId PeekNext (void) const; + virtual EventId RemoveNext (void); + virtual bool Remove (const EventId &ev); + private: inline bool IsLower (Scheduler::EventKey const*a, Scheduler::EventKey const*b) const; typedef std::list > Events; diff --git a/src/simulator/scheduler-map.cc b/src/simulator/scheduler-map.cc index 59be61ca7..e73abf4cf 100644 --- a/src/simulator/scheduler-map.cc +++ b/src/simulator/scheduler-map.cc @@ -88,9 +88,11 @@ SchedulerMap::EventKeyCompare::operator () (struct EventKey const&a, struct Even void -SchedulerMap::RealInsert (EventId id) +SchedulerMap::Insert (const EventId &id) { - EventImpl *event = id.GetEventImpl (); + // acquire a single ref + EventImpl *event = id.PeekEventImpl (); + event->Ref (); Scheduler::EventKey key; key.m_ts = id.GetTs (); key.m_uid = id.GetUid (); @@ -100,33 +102,38 @@ SchedulerMap::RealInsert (EventId id) } bool -SchedulerMap::RealIsEmpty (void) const +SchedulerMap::IsEmpty (void) const { return m_list.empty (); } EventId -SchedulerMap::RealPeekNext (void) const +SchedulerMap::PeekNext (void) const { EventMapCI i = m_list.begin (); NS_ASSERT (i != m_list.end ()); return EventId (i->second, i->first.m_ts, i->first.m_uid); } -void -SchedulerMap::RealRemoveNext (void) +EventId +SchedulerMap::RemoveNext (void) { - m_list.erase (m_list.begin ()); + EventMapI i = m_list.begin (); + std::pair next = *i; + m_list.erase (i); + return EventId (Ptr (next.second, false), next.first.m_ts, next.first.m_uid); } bool -SchedulerMap::RealRemove (EventId id) +SchedulerMap::Remove (const EventId &id) { Scheduler::EventKey key; key.m_ts = id.GetTs (); key.m_uid = id.GetUid (); EventMapI i = m_list.find (key); - NS_ASSERT (i->second == id.GetEventImpl ()); + NS_ASSERT (i->second == id.PeekEventImpl ()); + // release single ref. + i->second->Unref (); m_list.erase (i); return true; } diff --git a/src/simulator/scheduler-map.h b/src/simulator/scheduler-map.h index b8fb2cfa5..dbbc7fdbd 100644 --- a/src/simulator/scheduler-map.h +++ b/src/simulator/scheduler-map.h @@ -36,12 +36,12 @@ public: SchedulerMap (); virtual ~SchedulerMap (); + virtual void Insert (const EventId &id); + virtual bool IsEmpty (void) const; + virtual EventId PeekNext (void) const; + virtual EventId RemoveNext (void); + virtual bool Remove (const EventId &ev); private: - virtual void RealInsert (EventId id); - virtual bool RealIsEmpty (void) const; - virtual EventId RealPeekNext (void) const; - virtual void RealRemoveNext (void); - virtual bool RealRemove (EventId ev); class EventKeyCompare { public: diff --git a/src/simulator/scheduler.cc b/src/simulator/scheduler.cc index f8190bb39..4ba1d01b6 100644 --- a/src/simulator/scheduler.cc +++ b/src/simulator/scheduler.cc @@ -27,33 +27,4 @@ namespace ns3 { Scheduler::~Scheduler () {} -void -Scheduler::Insert (EventId id) -{ - return RealInsert (id); -} -bool -Scheduler::IsEmpty (void) const -{ - return RealIsEmpty (); -} -EventId -Scheduler::PeekNext (void) const -{ - NS_ASSERT (!RealIsEmpty ()); - return RealPeekNext (); -} -void -Scheduler::RemoveNext (void) -{ - NS_ASSERT (!RealIsEmpty ()); - return RealRemoveNext (); -} -bool -Scheduler::Remove (EventId id) -{ - NS_ASSERT (!RealIsEmpty ()); - return RealRemove (id); -} - }; // namespace ns3 diff --git a/src/simulator/scheduler.h b/src/simulator/scheduler.h index 0df7a77df..d8dea8672 100644 --- a/src/simulator/scheduler.h +++ b/src/simulator/scheduler.h @@ -27,21 +27,18 @@ namespace ns3 { -class EventImpl; - /** * \brief Maintain the event list * * This base class specifies the interface used to maintain the * event list. If you want to provide a new event list scheduler, * you need to create a subclass of this base class and implement - * all the private pure virtual methods defined here. Namely: - * - ns3::Scheduler::realInsert - * - ns3::Scheduler::realIsEmpty - * - ns3::Scheduler::realPeekNext - * - ns3::Scheduler::realPeekNextKey - * - ns3::Scheduler::realRemoveNext - * - ns3::Scheduler::realRemove + * all the pure virtual methods defined here. Namely: + * - ns3::Scheduler::Insert + * - ns3::Scheduler::IsEmpty + * - ns3::Scheduler::PeekNext + * - ns3::Scheduler::RemoveNext + * - ns3::Scheduler::Remove * * If you need to provide a new event list scheduler without * editing the main simulator class, you need to also implement @@ -60,46 +57,36 @@ class Scheduler { virtual ~Scheduler () = 0; - void Insert (EventId id); - bool IsEmpty (void) const; - EventId PeekNext (void) const; - void RemoveNext (void); - bool Remove (EventId); - -private: /** - * \param event event to store in the event list - * \param key timecode associated to this new event - * \returns an event id which identifies the event inserted + * \param id event to store in the event list * * This method takes ownership of the event pointer. */ - virtual void RealInsert (EventId id) = 0; + virtual void Insert (const EventId &id) = 0; /** * \returns true if the event list is empty and false otherwise. */ - virtual bool RealIsEmpty (void) const = 0; + virtual bool IsEmpty (void) const = 0; /** * \returns a pointer to the next earliest event. The caller * takes ownership of the returned pointer. * * This method cannot be invoked if the list is empty. */ - virtual EventId RealPeekNext (void) const = 0; + virtual EventId PeekNext (void) const = 0; /** * This method cannot be invoked if the list is empty. * Remove the next earliest event from the event list. */ - virtual void RealRemoveNext (void) = 0; + virtual EventId RemoveNext (void) = 0; /** * \param id the id of the event to remove - * \param key the timecode of the event removed - * \returns a pointer to the event removed. The caller - * takes ownership of the returned pointer. + * \returns true if the id was found and removed + * successfully, false otherwise. * * This methods cannot be invoked if the list is empty. */ - virtual bool RealRemove (EventId id) = 0; + virtual bool Remove (const EventId &id) = 0; }; }; // namespace ns3 diff --git a/src/simulator/simulator.cc b/src/simulator/simulator.cc index c133cb5a4..6ee0340fd 100644 --- a/src/simulator/simulator.cc +++ b/src/simulator/simulator.cc @@ -23,6 +23,7 @@ #include "scheduler.h" #include "event-impl.h" +#include "ns3/ptr.h" #include "ns3/assert.h" #include "ns3/default-value.h" @@ -61,12 +62,12 @@ public: Time Next (void) const; void Stop (void); void StopAt (Time const &time); - EventId Schedule (Time const &time, EventImpl *event); - EventId ScheduleNow (EventImpl *event); - EventId ScheduleDestroy (EventImpl *event); - void Remove (EventId ev); - void Cancel (EventId &ev); - bool IsExpired (EventId ev); + EventId Schedule (Time const &time, const Ptr &event); + EventId ScheduleNow (const Ptr &event); + EventId ScheduleDestroy (const Ptr &event); + void Remove (const EventId &ev); + void Cancel (const EventId &ev); + bool IsExpired (const EventId &ev); void Run (void); Time Now (void) const; @@ -114,13 +115,12 @@ SimulatorPrivate::~SimulatorPrivate () { while (!m_destroyEvents.empty ()) { - EventImpl *ev = m_destroyEvents.front ().GetEventImpl (); + Ptr ev = m_destroyEvents.front ().PeekEventImpl (); m_destroyEvents.pop_front (); TRACE ("handle destroy " << ev); if (!ev->IsCancelled ()) { ev->Invoke (); - delete ev; } } delete m_events; @@ -138,8 +138,7 @@ SimulatorPrivate::EnableLogTo (char const *filename) void SimulatorPrivate::ProcessOneEvent (void) { - EventId next = m_events->PeekNext (); - m_events->RemoveNext (); + EventId next = m_events->RemoveNext (); NS_ASSERT (next.GetTs () >= m_currentTs); --m_unscheduledEvents; @@ -151,9 +150,8 @@ SimulatorPrivate::ProcessOneEvent (void) { m_log << "e "<Invoke (); - delete event; } bool @@ -204,7 +202,7 @@ SimulatorPrivate::StopAt (Time const &at) m_stopAt = at.GetTimeStep (); } EventId -SimulatorPrivate::Schedule (Time const &time, EventImpl *event) +SimulatorPrivate::Schedule (Time const &time, const Ptr &event) { NS_ASSERT (time.IsPositive ()); NS_ASSERT (time >= TimeStep (m_currentTs)); @@ -221,7 +219,7 @@ SimulatorPrivate::Schedule (Time const &time, EventImpl *event) return id; } EventId -SimulatorPrivate::ScheduleNow (EventImpl *event) +SimulatorPrivate::ScheduleNow (const Ptr &event) { EventId id (event, m_currentTs, m_uid); if (m_logEnable) @@ -235,7 +233,7 @@ SimulatorPrivate::ScheduleNow (EventImpl *event) return id; } EventId -SimulatorPrivate::ScheduleDestroy (EventImpl *event) +SimulatorPrivate::ScheduleDestroy (const Ptr &event) { EventId id (event, m_currentTs, 2); m_destroyEvents.push_back (id); @@ -255,7 +253,7 @@ SimulatorPrivate::Now (void) const } void -SimulatorPrivate::Remove (EventId ev) +SimulatorPrivate::Remove (const EventId &ev) { if (ev.GetUid () == 2) { @@ -275,7 +273,7 @@ SimulatorPrivate::Remove (EventId ev) return; } m_events->Remove (ev); - delete ev.GetEventImpl (); + Cancel (ev); if (m_logEnable) { @@ -286,13 +284,16 @@ SimulatorPrivate::Remove (EventId ev) } void -SimulatorPrivate::Cancel (EventId &id) +SimulatorPrivate::Cancel (const EventId &id) { - id.Cancel (); + if (!IsExpired (id)) + { + id.PeekEventImpl ()->Cancel (); + } } bool -SimulatorPrivate::IsExpired (const EventId ev) +SimulatorPrivate::IsExpired (const EventId &ev) { if (ev.GetUid () == 2) { @@ -306,11 +307,11 @@ SimulatorPrivate::IsExpired (const EventId ev) } return true; } - if (ev.GetEventImpl () == 0 || + if (ev.PeekEventImpl () == 0 || ev.GetTs () < m_currentTs || (ev.GetTs () == m_currentTs && ev.GetUid () <= m_currentUid) || - ev.GetEventImpl ()->IsCancelled ()) + ev.PeekEventImpl ()->IsCancelled ()) { return true; } @@ -336,20 +337,20 @@ SimulatorPrivate *Simulator::m_priv = 0; void Simulator::SetLinkedList (void) { - Bind ("Scheduler", "List"); + DefaultValue::Bind ("Scheduler", "List"); } void Simulator::SetBinaryHeap (void) { - Bind ("Scheduler", "BinaryHeap"); + DefaultValue::Bind ("Scheduler", "BinaryHeap"); } void Simulator::SetStdMap (void) { - Bind ("Scheduler", "Map"); + DefaultValue::Bind ("Scheduler", "Map"); } void Simulator::SetExternal (const std::string &external) { - Bind ("Scheduler", external); + DefaultValue::Bind ("Scheduler", external); } void Simulator::EnableLogTo (char const *filename) { @@ -411,39 +412,39 @@ Simulator::Now (void) return GetPriv ()->Now (); } -EventImpl * +Ptr Simulator::MakeEvent (void (*f) (void)) { // zero arg version class EventFunctionImpl0 : public EventImpl { public: - typedef void (*F)(void); + typedef void (*F)(void); - EventFunctionImpl0 (F function) - : m_function (function) - {} - virtual ~EventFunctionImpl0 () {} + EventFunctionImpl0 (F function) + : m_function (function) + {} + virtual ~EventFunctionImpl0 () {} protected: - virtual void Notify (void) { - (*m_function) (); - } + virtual void Notify (void) { + (*m_function) (); + } private: F m_function; } *ev = new EventFunctionImpl0 (f); - return ev; + return Ptr (ev, false); } EventId -Simulator::Schedule (Time const &time, EventImpl *ev) +Simulator::Schedule (Time const &time, const Ptr &ev) { return GetPriv ()->Schedule (Now () + time, ev); } EventId -Simulator::ScheduleNow (EventImpl *ev) +Simulator::ScheduleNow (const Ptr &ev) { return GetPriv ()->ScheduleNow (ev); } EventId -Simulator::ScheduleDestroy (EventImpl *ev) +Simulator::ScheduleDestroy (const Ptr &ev) { return GetPriv ()->ScheduleDestroy (ev); } @@ -465,18 +466,18 @@ Simulator::ScheduleDestroy (void (*f) (void)) void -Simulator::Remove (EventId ev) +Simulator::Remove (const EventId &ev) { return GetPriv ()->Remove (ev); } void -Simulator::Cancel (EventId &ev) +Simulator::Cancel (const EventId &ev) { return GetPriv ()->Cancel (ev); } bool -Simulator::IsExpired (EventId id) +Simulator::IsExpired (const EventId &id) { return GetPriv ()->IsExpired (id); } @@ -538,13 +539,14 @@ class SimulatorTests : public Test { public: SimulatorTests (); // only here for testing of Ptr<> - void Ref (void); - void Unref (void); + void Ref (void) const; + void Unref (void) const; virtual ~SimulatorTests (); virtual bool RunTests (void); private: uint64_t NowUs (); bool RunOneTest (void); + void RunTestsConst (void) const; void A (int a); void B (int b); void C (int c); @@ -565,6 +567,24 @@ private: void cbaz3 (const int &, const int &, const int &); void cbaz4 (const int &, const int &, const int &, const int &); void cbaz5 (const int &, const int &, const int &, const int &, const int &); + + void bar0c (void) const; + void bar1c (int) const; + void bar2c (int, int) const; + void bar3c (int, int, int) const; + void bar4c (int, int, int, int) const; + void bar5c (int, int, int, int, int) const; + void baz1c (int &) const; + void baz2c (int &, int &) const; + void baz3c (int &, int &, int &) const; + void baz4c (int &, int &, int &, int &) const; + void baz5c (int &, int &, int &, int &, int &) const; + void cbaz1c (const int &) const; + void cbaz2c (const int &, const int &) const; + void cbaz3c (const int &, const int &, const int &) const; + void cbaz4c (const int &, const int &, const int &, const int &) const; + void cbaz5c (const int &, const int &, const int &, const int &, const int &) const; + void destroy (void); bool m_b; @@ -582,10 +602,10 @@ SimulatorTests::SimulatorTests () SimulatorTests::~SimulatorTests () {} void -SimulatorTests::Ref (void) +SimulatorTests::Ref (void) const {} void -SimulatorTests::Unref (void) +SimulatorTests::Unref (void) const {} uint64_t SimulatorTests::NowUs (void) @@ -688,6 +708,57 @@ void SimulatorTests::cbaz5 (const int &, const int &, const int &, const int &, const int &) {} +void +SimulatorTests::bar0c (void) const +{} +void +SimulatorTests::bar1c (int) const +{} +void +SimulatorTests::bar2c (int, int) const +{} +void +SimulatorTests::bar3c (int, int, int) const +{} +void +SimulatorTests::bar4c (int, int, int, int) const +{} +void +SimulatorTests::bar5c (int, int, int, int, int) const +{} + +void +SimulatorTests::baz1c (int &) const +{} +void +SimulatorTests::baz2c (int &, int &) const +{} +void +SimulatorTests::baz3c (int &, int &, int &) const +{} +void +SimulatorTests::baz4c (int &, int &, int &, int &) const +{} +void +SimulatorTests::baz5c (int &, int &, int &, int &, int &) const +{} + +void +SimulatorTests::cbaz1c (const int &) const +{} +void +SimulatorTests::cbaz2c (const int &, const int &) const +{} +void +SimulatorTests::cbaz3c (const int &, const int &, const int &) const +{} +void +SimulatorTests::cbaz4c (const int &, const int &, const int &, const int &) const +{} +void +SimulatorTests::cbaz5c (const int &, const int &, const int &, const int &, const int &) const +{} + bool SimulatorTests::RunOneTest (void) { @@ -722,6 +793,80 @@ SimulatorTests::RunOneTest (void) } return ok; } +void +SimulatorTests::RunTestsConst (void) const +{ + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar0c, this); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar1c, this, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar2c, this, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar3c, this, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar4c, this, 0, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar5c, this, 0, 0, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar0c, Ptr (this)); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar1c, Ptr (this), 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar2c, Ptr (this), 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar3c, Ptr (this), 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar4c, Ptr (this), 0, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::bar5c, Ptr (this), 0, 0, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::cbaz1c, this, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::cbaz2c, this, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::cbaz3c, this, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::cbaz4c, this, 0, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::cbaz5c, this, 0, 0, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::bar0c, this); + Simulator::ScheduleNow (&SimulatorTests::bar1c, this, 0); + Simulator::ScheduleNow (&SimulatorTests::bar2c, this, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::bar3c, this, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::bar4c, this, 0, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::bar5c, this, 0, 0, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::cbaz1c, this, 0); + Simulator::ScheduleNow (&SimulatorTests::cbaz2c, this, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::cbaz3c, this, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::cbaz4c, this, 0, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::cbaz5c, this, 0, 0, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::bar0c, Ptr (this)); + Simulator::ScheduleNow (&SimulatorTests::bar1c, Ptr (this), 0); + Simulator::ScheduleNow (&SimulatorTests::bar2c, Ptr (this), 0, 0); + Simulator::ScheduleNow (&SimulatorTests::bar3c, Ptr (this), 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::bar4c, Ptr (this), 0, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::bar5c, Ptr (this), 0, 0, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar0c, this); + Simulator::ScheduleDestroy (&SimulatorTests::bar1c, this, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar2c, this, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar3c, this, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar4c, this, 0, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar5c, this, 0, 0, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::cbaz1c, this, 0); + Simulator::ScheduleDestroy (&SimulatorTests::cbaz2c, this, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::cbaz3c, this, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::cbaz4c, this, 0, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::cbaz5c, this, 0, 0, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar0c, Ptr (this)); + Simulator::ScheduleDestroy (&SimulatorTests::bar1c, Ptr (this), 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar2c, Ptr (this), 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar3c, Ptr (this), 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar4c, Ptr (this), 0, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::bar5c, Ptr (this), 0, 0, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::baz1c, this, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::baz2c, this, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::baz3c, this, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::baz4c, this, 0, 0, 0, 0); + Simulator::Schedule (Seconds (0.0), &SimulatorTests::baz5c, this, 0, 0, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::baz1c, this, 0); + Simulator::ScheduleNow (&SimulatorTests::baz2c, this, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::baz3c, this, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::baz4c, this, 0, 0, 0, 0); + Simulator::ScheduleNow (&SimulatorTests::baz5c, this, 0, 0, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::baz1c, this, 0); + Simulator::ScheduleDestroy (&SimulatorTests::baz2c, this, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::baz3c, this, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::baz4c, this, 0, 0, 0, 0); + Simulator::ScheduleDestroy (&SimulatorTests::baz5c, this, 0, 0, 0, 0, 0); + + Simulator::Run (); + Simulator::Destroy (); +} + bool SimulatorTests::RunTests (void) { @@ -869,6 +1014,8 @@ SimulatorTests::RunTests (void) Simulator::ScheduleDestroy (&SimulatorTests::baz5, this, 0, 0, 0, 0, 0); #endif + RunTestsConst (); + EventId nowId = Simulator::ScheduleNow (&foo0); m_destroyId = Simulator::ScheduleDestroy (&SimulatorTests::destroy, this); if (m_destroyId.IsExpired ()) @@ -884,6 +1031,22 @@ SimulatorTests::RunTests (void) ok = false; } + EventId anId = Simulator::ScheduleNow (&foo0); + EventId anotherId = anId; + if (anId.IsExpired () || anotherId.IsExpired ()) + { + ok = false; + } + Simulator::Remove (anId); + if (!anId.IsExpired () || !anotherId.IsExpired ()) + { + ok = false; + } + + Simulator::Run (); + Simulator::Destroy (); + + return ok; } diff --git a/src/simulator/simulator.h b/src/simulator/simulator.h index a7eea7600..cdf2f8b4e 100644 --- a/src/simulator/simulator.h +++ b/src/simulator/simulator.h @@ -163,8 +163,8 @@ public: * @param obj the object on which to invoke the member method * @returns an id for the scheduled event. */ - template - static EventId Schedule (Time const &time, void (T::*mem_ptr) (void), OBJ obj); + template + static EventId Schedule (Time const &time, MEM mem_ptr, OBJ obj); /** * @param time the relative expiration time of the event. * @param mem_ptr member method pointer to invoke @@ -172,8 +172,8 @@ public: * @param a1 the first argument to pass to the invoked method * @returns an id for the scheduled event. */ - template - static EventId Schedule (Time const &time, void (T::*mem_ptr) (U1), OBJ obj, T1 a1); + template + static EventId Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1); /** * @param time the relative expiration time of the event. * @param mem_ptr member method pointer to invoke @@ -182,8 +182,8 @@ public: * @param a2 the second argument to pass to the invoked method * @returns an id for the scheduled event. */ - template - static EventId Schedule (Time const &time, void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 a2); + template + static EventId Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1, T2 a2); /** * @param time the relative expiration time of the event. * @param mem_ptr member method pointer to invoke @@ -193,10 +193,9 @@ public: * @param a3 the third argument to pass to the invoked method * @returns an id for the scheduled event. */ - template - static EventId Schedule (Time const &time, void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, T2 a2, T3 a3); + static EventId Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3); /** * @param time the relative expiration time of the event. * @param mem_ptr member method pointer to invoke @@ -207,10 +206,9 @@ public: * @param a4 the fourth argument to pass to the invoked method * @returns an id for the scheduled event. */ - template - static EventId Schedule (Time const &time, void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4); + static EventId Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4); /** * @param time the relative expiration time of the event. * @param mem_ptr member method pointer to invoke @@ -222,10 +220,9 @@ public: * @param a5 the fifth argument to pass to the invoked method * @returns an id for the scheduled event. */ - template - static EventId Schedule (Time const &time, void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, + static EventId Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); /** * @param time the relative expiration time of the event. @@ -295,27 +292,25 @@ public: * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method */ - template - static EventId ScheduleNow (void (T::*mem_ptr) (void), OBJ obj); + template + static EventId ScheduleNow (MEM mem_ptr, OBJ obj); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method * @param a1 the first argument to pass to the invoked method */ - template - static EventId ScheduleNow (void (T::*mem_ptr) (U1), OBJ obj, T1 a1); + static EventId ScheduleNow (MEM mem_ptr, OBJ obj, T1 a1); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method * @param a1 the first argument to pass to the invoked method * @param a2 the second argument to pass to the invoked method */ - template - static EventId ScheduleNow (void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 a2); + static EventId ScheduleNow (MEM mem_ptr, OBJ obj, T1 a1, T2 a2); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method @@ -323,10 +318,9 @@ public: * @param a2 the second argument to pass to the invoked method * @param a3 the third argument to pass to the invoked method */ - template - static EventId ScheduleNow (void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, T2 a2, T3 a3); + static EventId ScheduleNow (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method @@ -335,11 +329,10 @@ public: * @param a3 the third argument to pass to the invoked method * @param a4 the fourth argument to pass to the invoked method */ - template - static EventId ScheduleNow (void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, - T1 a1, T2 a2, T3 a3, T4 a4); + static EventId ScheduleNow (MEM mem_ptr, OBJ obj, + T1 a1, T2 a2, T3 a3, T4 a4); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method @@ -349,11 +342,10 @@ public: * @param a4 the fourth argument to pass to the invoked method * @param a5 the fifth argument to pass to the invoked method */ - template - static EventId ScheduleNow (void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, - T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); + static EventId ScheduleNow (MEM mem_ptr, OBJ obj, + T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); /** * @param f the function to invoke */ @@ -414,27 +406,25 @@ public: * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method */ - template - static EventId ScheduleDestroy (void (T::*mem_ptr) (void), OBJ obj); + template + static EventId ScheduleDestroy (MEM mem_ptr, OBJ obj); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method * @param a1 the first argument to pass to the invoked method */ - template - static EventId ScheduleDestroy (void (T::*mem_ptr) (U1), OBJ obj, T1 a1); + static EventId ScheduleDestroy (MEM mem_ptr, OBJ obj, T1 a1); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method * @param a1 the first argument to pass to the invoked method * @param a2 the second argument to pass to the invoked method */ - template - static EventId ScheduleDestroy (void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 a2); + static EventId ScheduleDestroy (MEM mem_ptr, OBJ obj, T1 a1, T2 a2); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method @@ -442,10 +432,9 @@ public: * @param a2 the second argument to pass to the invoked method * @param a3 the third argument to pass to the invoked method */ - template - static EventId ScheduleDestroy (void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, T2 a2, T3 a3); + static EventId ScheduleDestroy (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method @@ -454,11 +443,10 @@ public: * @param a3 the third argument to pass to the invoked method * @param a4 the fourth argument to pass to the invoked method */ - template - static EventId ScheduleDestroy (void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, - T1 a1, T2 a2, T3 a3, T4 a4); + static EventId ScheduleDestroy (MEM mem_ptr, OBJ obj, + T1 a1, T2 a2, T3 a3, T4 a4); /** * @param mem_ptr member method pointer to invoke * @param obj the object on which to invoke the member method @@ -468,11 +456,10 @@ public: * @param a4 the fourth argument to pass to the invoked method * @param a5 the fifth argument to pass to the invoked method */ - template - static EventId ScheduleDestroy (void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, - T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); + static EventId ScheduleDestroy (MEM mem_ptr, OBJ obj, + T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); /** * @param f the function to invoke */ @@ -534,7 +521,7 @@ public: * * @param id the event to remove from the list of scheduled events. */ - static void Remove (EventId id); + static void Remove (const EventId &id); /** * Set the cancel bit on this event: the event's associated function * will not be invoked when it expires. @@ -547,7 +534,7 @@ public: * * @param id the event to cancel */ - static void Cancel (EventId &id); + static void Cancel (const EventId &id); /** * This method has O(1) complexity. * Note that it is not possible to test for the expiration of @@ -560,7 +547,7 @@ public: * @param id the event to test for expiration * @returns true if the event has expired, false otherwise. */ - static bool IsExpired (const EventId id); + static bool IsExpired (const EventId &id); /** * Return the "current simulation time". */ @@ -569,50 +556,45 @@ private: Simulator (); ~Simulator (); - template - static EventImpl *MakeEvent (void (T::*mem_ptr) (void), OBJ obj); - template + static Ptr MakeEvent (MEM mem_ptr, OBJ obj); + template - static EventImpl *MakeEvent (void (T::*mem_ptr) (U1), OBJ obj, T1 a1); - template MakeEvent (MEM mem_ptr, OBJ obj, T1 a1); + template - static EventImpl *MakeEvent (void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 a2); - template MakeEvent (MEM mem_ptr, OBJ obj, T1 a1, T2 a2); + template - static EventImpl *MakeEvent (void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, T2 a2, T3 a3); - template MakeEvent (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3); + template - static EventImpl *MakeEvent (void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4); - template MakeEvent (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4); + template - static EventImpl *MakeEvent (void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, - T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); - static EventImpl *MakeEvent (void (*f) (void)); + static Ptr MakeEvent (MEM mem_ptr, OBJ obj, + T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); + static Ptr MakeEvent (void (*f) (void)); template - static EventImpl *MakeEvent (void (*f) (U1), T1 a1); + static Ptr MakeEvent (void (*f) (U1), T1 a1); template - static EventImpl *MakeEvent (void (*f) (U1,U2), T1 a1, T2 a2); + static Ptr MakeEvent (void (*f) (U1,U2), T1 a1, T2 a2); template - static EventImpl *MakeEvent (void (*f) (U1,U2,U3), T1 a1, T2 a2, T3 a3); + static Ptr MakeEvent (void (*f) (U1,U2,U3), T1 a1, T2 a2, T3 a3); template - static EventImpl *MakeEvent (void (*f) (U1,U2,U3,U4), T1 a1, T2 a2, T3 a3, T4 a4); + static Ptr MakeEvent (void (*f) (U1,U2,U3,U4), T1 a1, T2 a2, T3 a3, T4 a4); template - static EventImpl *MakeEvent (void (*f) (U1,U2,U3,U4,U5), T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); + static Ptr MakeEvent (void (*f) (U1,U2,U3,U4,U5), T1 a1, T2 a2, T3 a3, T4 a4, T5 a5); static SimulatorPrivate *GetPriv (void); - static EventId Schedule (Time const &time, EventImpl *event); - static EventId ScheduleDestroy (EventImpl *event); - static EventId ScheduleNow (EventImpl *event); + static EventId Schedule (Time const &time, const Ptr &event); + static EventId ScheduleDestroy (const Ptr &event); + static EventId ScheduleNow (const Ptr &event); static SimulatorPrivate *m_priv; }; @@ -649,14 +631,13 @@ struct EventMemberImplObjTraits } }; -template -EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (void), OBJ obj) +template +Ptr Simulator::MakeEvent (MEM mem_ptr, OBJ obj) { // zero argument version class EventMemberImpl0 : public EventImpl { public: - typedef void (T::*F)(void); - EventMemberImpl0 (OBJ obj, F function) + EventMemberImpl0 (OBJ obj, MEM function) : m_obj (obj), m_function (function) {} @@ -666,22 +647,20 @@ EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (void), OBJ obj) (EventMemberImplObjTraits::GetReference (m_obj).*m_function) (); } OBJ m_obj; - F m_function; - } *ev = new EventMemberImpl0 (obj, mem_ptr); - return ev; + MEM m_function; + } * ev = new EventMemberImpl0 (obj, mem_ptr); + return Ptr (ev, false); } -template -EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1), OBJ obj, T1 a1) +Ptr Simulator::MakeEvent (MEM mem_ptr, OBJ obj, T1 a1) { // one argument version class EventMemberImpl1 : public EventImpl { public: - typedef void (T::*F)(U1); - EventMemberImpl1 (OBJ obj, F function, T1 a1) + EventMemberImpl1 (OBJ obj, MEM function, T1 a1) : m_obj (obj), m_function (function), m_a1 (a1) @@ -693,23 +672,20 @@ EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1), OBJ obj, T1 a1) (EventMemberImplObjTraits::GetReference (m_obj).*m_function) (m_a1); } OBJ m_obj; - F m_function; + MEM m_function; typename TypeTraits::ReferencedType m_a1; } *ev = new EventMemberImpl1 (obj, mem_ptr, a1); - return ev; + return Ptr (ev, false); } -template -EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 a2) +Ptr Simulator::MakeEvent (MEM mem_ptr, OBJ obj, T1 a1, T2 a2) { // two argument version class EventMemberImpl2 : public EventImpl { public: - typedef void (T::*F)(U1, U2); - - EventMemberImpl2 (OBJ obj, F function, T1 a1, T2 a2) + EventMemberImpl2 (OBJ obj, MEM function, T1 a1, T2 a2) : m_obj (obj), m_function (function), m_a1 (a1), @@ -722,24 +698,21 @@ EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 (EventMemberImplObjTraits::GetReference (m_obj).*m_function) (m_a1, m_a2); } OBJ m_obj; - F m_function; + MEM m_function; typename TypeTraits::ReferencedType m_a1; typename TypeTraits::ReferencedType m_a2; } *ev = new EventMemberImpl2 (obj, mem_ptr, a1, a2); - return ev; + return Ptr (ev, false); } -template -EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, T2 a2, T3 a3) +Ptr Simulator::MakeEvent (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3) { // three argument version class EventMemberImpl3 : public EventImpl { public: - typedef void (T::*F)(U1,U2,U3); - - EventMemberImpl3 (OBJ obj, F function, T1 a1, T2 a2, T3 a3) + EventMemberImpl3 (OBJ obj, MEM function, T1 a1, T2 a2, T3 a3) : m_obj (obj), m_function (function), m_a1 (a1), @@ -753,25 +726,22 @@ EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, (EventMemberImplObjTraits::GetReference (m_obj).*m_function) (m_a1, m_a2, m_a3); } OBJ m_obj; - F m_function; + MEM m_function; typename TypeTraits::ReferencedType m_a1; typename TypeTraits::ReferencedType m_a2; typename TypeTraits::ReferencedType m_a3; } *ev = new EventMemberImpl3 (obj, mem_ptr, a1, a2, a3); - return ev; + return Ptr (ev, false); } -template -EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4) +Ptr Simulator::MakeEvent (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4) { // four argument version class EventMemberImpl4 : public EventImpl { public: - typedef void (T::*F)(U1, U2, U3, U4); - - EventMemberImpl4 (OBJ obj, F function, T1 a1, T2 a2, T3 a3, T4 a4) + EventMemberImpl4 (OBJ obj, MEM function, T1 a1, T2 a2, T3 a3, T4 a4) : m_obj (obj), m_function (function), m_a1 (a1), @@ -786,27 +756,24 @@ EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, T1 a (EventMemberImplObjTraits::GetReference (m_obj).*m_function) (m_a1, m_a2, m_a3, m_a4); } OBJ m_obj; - F m_function; + MEM m_function; typename TypeTraits::ReferencedType m_a1; typename TypeTraits::ReferencedType m_a2; typename TypeTraits::ReferencedType m_a3; typename TypeTraits::ReferencedType m_a4; } *ev = new EventMemberImpl4 (obj, mem_ptr, a1, a2, a3, a4); - return ev; + return Ptr (ev, false); } -template -EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, - T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) +Ptr Simulator::MakeEvent (MEM mem_ptr, OBJ obj, + T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { // five argument version class EventMemberImpl5 : public EventImpl { public: - typedef void (T::*F)(U1, U2, U3, U4, U5); - - EventMemberImpl5 (OBJ obj, F function, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) + EventMemberImpl5 (OBJ obj, MEM function, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) : m_obj (obj), m_function (function), m_a1 (a1), @@ -822,18 +789,18 @@ EventImpl *Simulator::MakeEvent (void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, (EventMemberImplObjTraits::GetReference (m_obj).*m_function) (m_a1, m_a2, m_a3, m_a4, m_a5); } OBJ m_obj; - F m_function; + MEM m_function; typename TypeTraits::ReferencedType m_a1; typename TypeTraits::ReferencedType m_a2; typename TypeTraits::ReferencedType m_a3; typename TypeTraits::ReferencedType m_a4; typename TypeTraits::ReferencedType m_a5; } *ev = new EventMemberImpl5 (obj, mem_ptr, a1, a2, a3, a4, a5); - return ev; + return Ptr (ev, false); } template -EventImpl *Simulator::MakeEvent (void (*f) (U1), T1 a1) +Ptr Simulator::MakeEvent (void (*f) (U1), T1 a1) { // one arg version class EventFunctionImpl1 : public EventImpl { @@ -852,12 +819,12 @@ EventImpl *Simulator::MakeEvent (void (*f) (U1), T1 a1) } F m_function; typename TypeTraits::ReferencedType m_a1; - } *ev = new EventFunctionImpl1(f, a1); - return ev; + } *ev = new EventFunctionImpl1 (f, a1); + return Ptr (ev, false); } template -EventImpl *Simulator::MakeEvent (void (*f) (U1,U2), T1 a1, T2 a2) +Ptr Simulator::MakeEvent (void (*f) (U1,U2), T1 a1, T2 a2) { // two arg version class EventFunctionImpl2 : public EventImpl { @@ -879,12 +846,12 @@ EventImpl *Simulator::MakeEvent (void (*f) (U1,U2), T1 a1, T2 a2) typename TypeTraits::ReferencedType m_a1; typename TypeTraits::ReferencedType m_a2; } *ev = new EventFunctionImpl2 (f, a1, a2); - return ev; + return Ptr (ev, false); } template -EventImpl *Simulator::MakeEvent (void (*f) (U1,U2,U3), T1 a1, T2 a2, T3 a3) +Ptr Simulator::MakeEvent (void (*f) (U1,U2,U3), T1 a1, T2 a2, T3 a3) { // three arg version class EventFunctionImpl3 : public EventImpl { @@ -908,12 +875,12 @@ EventImpl *Simulator::MakeEvent (void (*f) (U1,U2,U3), T1 a1, T2 a2, T3 a3) typename TypeTraits::ReferencedType m_a2; typename TypeTraits::ReferencedType m_a3; } *ev = new EventFunctionImpl3 (f, a1, a2, a3); - return ev; + return Ptr (ev, false); } template -EventImpl *Simulator::MakeEvent (void (*f) (U1,U2,U3,U4), T1 a1, T2 a2, T3 a3, T4 a4) +Ptr Simulator::MakeEvent (void (*f) (U1,U2,U3,U4), T1 a1, T2 a2, T3 a3, T4 a4) { // four arg version class EventFunctionImpl4 : public EventImpl { @@ -939,12 +906,12 @@ EventImpl *Simulator::MakeEvent (void (*f) (U1,U2,U3,U4), T1 a1, T2 a2, T3 a3, T typename TypeTraits::ReferencedType m_a3; typename TypeTraits::ReferencedType m_a4; } *ev = new EventFunctionImpl4 (f, a1, a2, a3, a4); - return ev; + return Ptr (ev, false); } template -EventImpl *Simulator::MakeEvent (void (*f) (U1,U2,U3,U4,U5), T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) +Ptr Simulator::MakeEvent (void (*f) (U1,U2,U3,U4,U5), T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { // five arg version class EventFunctionImpl5 : public EventImpl { @@ -972,53 +939,48 @@ EventImpl *Simulator::MakeEvent (void (*f) (U1,U2,U3,U4,U5), T1 a1, T2 a2, T3 a3 typename TypeTraits::ReferencedType m_a4; typename TypeTraits::ReferencedType m_a5; } *ev = new EventFunctionImpl5 (f, a1, a2, a3, a4, a5); - return ev; + return Ptr (ev, false); } -template -EventId Simulator::Schedule (Time const &time, void (T::*mem_ptr) (void), OBJ obj) +template +EventId Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj) { return Schedule (time, MakeEvent (mem_ptr, obj)); } -template -EventId Simulator::Schedule (Time const &time, void (T::*mem_ptr) (U1), OBJ obj, T1 a1) +EventId Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1) { return Schedule (time, MakeEvent (mem_ptr, obj, a1)); } -template -EventId Simulator::Schedule (Time const &time, void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 a2) +EventId Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1, T2 a2) { return Schedule (time, MakeEvent (mem_ptr, obj, a1, a2)); } -template -EventId Simulator::Schedule (Time const &time, void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, T2 a2, T3 a3) +EventId Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3) { return Schedule (time, MakeEvent (mem_ptr, obj, a1, a2, a3)); } -template -EventId Simulator::Schedule (Time const &time, void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4) +EventId Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4) { return Schedule (time, MakeEvent (mem_ptr, obj, a1, a2, a3, a4)); } -template -EventId Simulator::Schedule (Time const &time, void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, - T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) +EventId Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj, + T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { return Schedule (time, MakeEvent (mem_ptr, obj, a1, a2, a3, a4, a5)); } @@ -1060,55 +1022,50 @@ EventId Simulator::Schedule (Time const &time, void (*f) (U1,U2,U3,U4,U5), T1 a1 -template +template EventId -Simulator::ScheduleNow (void (T::*mem_ptr) (void), OBJ obj) +Simulator::ScheduleNow (MEM mem_ptr, OBJ obj) { return ScheduleNow (MakeEvent (mem_ptr, obj)); } -template EventId -Simulator::ScheduleNow (void (T::*mem_ptr) (U1), OBJ obj, T1 a1) +Simulator::ScheduleNow (MEM mem_ptr, OBJ obj, T1 a1) { return ScheduleNow (MakeEvent (mem_ptr, obj, a1)); } -template EventId -Simulator::ScheduleNow (void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 a2) +Simulator::ScheduleNow (MEM mem_ptr, OBJ obj, T1 a1, T2 a2) { return ScheduleNow (MakeEvent (mem_ptr, obj, a1, a2)); } -template EventId -Simulator::ScheduleNow (void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, T2 a2, T3 a3) +Simulator::ScheduleNow (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3) { return ScheduleNow (MakeEvent (mem_ptr, obj, a1, a2, a3)); } -template EventId -Simulator::ScheduleNow (void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4) +Simulator::ScheduleNow (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4) { return ScheduleNow (MakeEvent (mem_ptr, obj, a1, a2, a3, a4)); } -template EventId -Simulator::ScheduleNow (void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, +Simulator::ScheduleNow (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { return ScheduleNow (MakeEvent (mem_ptr, obj, a1, a2, a3, a4, a5)); @@ -1156,55 +1113,50 @@ Simulator::ScheduleNow (void (*f) (U1,U2,U3,U4,U5), T1 a1, T2 a2, T3 a3, T4 a4, -template +template EventId -Simulator::ScheduleDestroy (void (T::*mem_ptr) (void), OBJ obj) +Simulator::ScheduleDestroy (MEM mem_ptr, OBJ obj) { return ScheduleDestroy (MakeEvent (mem_ptr, obj)); } -template EventId -Simulator::ScheduleDestroy (void (T::*mem_ptr) (U1), OBJ obj, T1 a1) +Simulator::ScheduleDestroy (MEM mem_ptr, OBJ obj, T1 a1) { return ScheduleDestroy (MakeEvent (mem_ptr, obj, a1)); } -template EventId -Simulator::ScheduleDestroy (void (T::*mem_ptr) (U1,U2), OBJ obj, T1 a1, T2 a2) +Simulator::ScheduleDestroy (MEM mem_ptr, OBJ obj, T1 a1, T2 a2) { return ScheduleDestroy (MakeEvent (mem_ptr, obj, a1, a2)); } -template EventId -Simulator::ScheduleDestroy (void (T::*mem_ptr) (U1,U2,U3), OBJ obj, T1 a1, T2 a2, T3 a3) +Simulator::ScheduleDestroy (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3) { return ScheduleDestroy (MakeEvent (mem_ptr, obj, a1, a2, a3)); } -template EventId -Simulator::ScheduleDestroy (void (T::*mem_ptr) (U1,U2,U3,U4), OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4) +Simulator::ScheduleDestroy (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4) { return ScheduleDestroy (MakeEvent (mem_ptr, obj, a1, a2, a3, a4)); } -template EventId -Simulator::ScheduleDestroy (void (T::*mem_ptr) (U1,U2,U3,U4,U5), OBJ obj, +Simulator::ScheduleDestroy (MEM mem_ptr, OBJ obj, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { return ScheduleDestroy (MakeEvent (mem_ptr, obj, a1, a2, a3, a4, a5)); diff --git a/src/simulator/time.cc b/src/simulator/time.cc index 4adc49cc3..8c0e43e35 100644 --- a/src/simulator/time.cc +++ b/src/simulator/time.cc @@ -414,12 +414,12 @@ bool TimeTests::RunTests (void) TimeStepPrecision::Set (TimeStepPrecision::NS); - Bind ("TimeStepPrecision", "S"); - Bind ("TimeStepPrecision", "MS"); - Bind ("TimeStepPrecision", "US"); - Bind ("TimeStepPrecision", "NS"); - Bind ("TimeStepPrecision", "PS"); - Bind ("TimeStepPrecision", "FS"); + DefaultValue::Bind ("TimeStepPrecision", "S"); + DefaultValue::Bind ("TimeStepPrecision", "MS"); + DefaultValue::Bind ("TimeStepPrecision", "US"); + DefaultValue::Bind ("TimeStepPrecision", "NS"); + DefaultValue::Bind ("TimeStepPrecision", "PS"); + DefaultValue::Bind ("TimeStepPrecision", "FS"); return ok; } diff --git a/src/simulator/wscript b/src/simulator/wscript index 670544e9a..01124f5ea 100644 --- a/src/simulator/wscript +++ b/src/simulator/wscript @@ -47,11 +47,7 @@ def configure(conf): def build(bld): - sim = bld.create_obj('cpp', 'shlib') - sim.name = 'ns3-simulator' - sim.target = sim.name - sim.uselib_local = ['ns3-core'] - + sim = bld.create_ns3_module('simulator', ['core']) sim.source = [ 'high-precision.cc', 'time.cc', diff --git a/src/wscript b/src/wscript index e15ee5972..38664ddd8 100644 --- a/src/wscript +++ b/src/wscript @@ -2,6 +2,7 @@ import os, os.path import shutil +import types import Action import Common @@ -16,13 +17,13 @@ all_modules = ( 'node', 'internet-node', 'devices/point-to-point', - 'devices/csma-cd', + 'devices/csma', 'applications', 'routing/olsr', + 'routing/global-routing', 'mobility', ) - def set_options(opt): opt.sub_options('simulator') @@ -39,23 +40,30 @@ def configure(conf): conf.sub_config('simulator') blddir = os.path.abspath(os.path.join(conf.m_blddir, conf.env.variant())) - for module in all_modules: - module_path = os.path.join(blddir, 'src', module) - conf.env.append_value('NS3_MODULE_PATH', module_path) - if Params.g_options.enable_rpath: - conf.env.append_value('RPATH', '-Wl,-rpath=%s' % (module_path,)) + conf.env['NS3_MODULE_PATH'] = [os.path.join(blddir)] + if Params.g_options.enable_rpath: + conf.env.append_value('RPATH', '-Wl,-rpath=%s' % (os.path.join(blddir),)) ## Used to link the 'run-tests' program with all of ns-3 code conf.env['NS3_MODULES'] = ['ns3-' + module.split('/')[-1] for module in all_modules] - +def create_ns3_module(bld, name, dependencies=()): + module = bld.create_obj('cpp', 'objects') + module.name = 'ns3-' + name + module.target = module.name + module.add_objects = ['ns3-' + dep for dep in dependencies] + module.env.append_value('CXXFLAGS', module.env['shlib_CXXFLAGS']) + return module + def build(bld): Object.register('ns3header', Ns3Header) Action.Action('ns3header', func=_ns3_headers_inst, color='BLUE') + bld.create_ns3_module = types.MethodType(create_ns3_module, bld) bld.add_subdirs(list(all_modules)) + class Ns3Header(Object.genobj): """A set of NS-3 header files""" def __init__(self, env=None): diff --git a/utils/wscript b/utils/wscript index 739861360..d64d46dc5 100644 --- a/utils/wscript +++ b/utils/wscript @@ -4,23 +4,15 @@ def build(bld): env = bld.env_of_name('default') - def create_ns_prog(name, source): - obj = bld.create_obj('cpp', 'program') - obj.target = name - obj.source = source - return obj - - unit_tests = create_ns_prog('run-tests', 'run-tests.cc') + unit_tests = bld.create_ns3_program('run-tests') unit_tests.install_var = 0 # do not install unit_tests.unit_test = 1 # runs on 'waf check' + unit_tests.source = 'run-tests.cc' ## link unit test program with all ns3 modules - unit_tests.uselib_local = env['NS3_MODULES'] + unit_tests.uselib_local = 'ns3' - obj = create_ns_prog('bench-simulator', 'bench-simulator.cc') - obj.uselib_local = "ns3-core ns3-common ns3-simulator" + obj = bld.create_ns3_program('bench-simulator', ['simulator']) + obj.source = 'bench-simulator.cc' - obj = create_ns_prog('replay-simulation', 'replay-simulation.cc') - obj.uselib_local = "ns3-core ns3-common ns3-simulator" - - obj = create_ns_prog('bench-event-collector', 'bench-event-collector.cc') - obj.uselib_local = "ns3-simulator" + obj = bld.create_ns3_program('replay-simulation', ['simulator']) + obj.source = 'replay-simulation.cc' diff --git a/waf b/waf new file mode 100755 index 000000000..94a2a37a2 --- /dev/null +++ b/waf @@ -0,0 +1,338 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005 (ita) + +import os, sys +if 'PSYCOWAF' in os.environ: + try: + import psyco + psyco.full() + except: + pass + +VERSION="1.1.1" +REVISION="1420549696" +INSTALL=sys.platform=='win32' and 'c:/temp' or '/usr/local' +cwd = os.getcwd() + +def decodeAscii85(s): + out=[] + app=out.append + s=''.join(s.split()).replace('z','!!!!!') + p1,p2=divmod(len(s), 5) + stop=5*p1 + p3,p4=s[0:stop],s[stop:] + for i in range(p1): + off=i*5 + a=ord(p3[off])-33 + b=ord(p3[off+1])-33 + c=ord(p3[off+2])-33 + d=ord(p3[off+3])-33 + e=ord(p3[off+4])-33 + num=(52200625L*a)+(614125*b)+(7225*c)+(85*d)+e + x,p=divmod(num,256) + x,o=divmod(x,256) + m,n=divmod(x,256) + app(chr(m)+chr(n)+chr(o)+chr(p)) + if p2: + while len(p4)<5: p4=p4+'!' + a=ord(p4[0])-33 + b=ord(p4[1])-33 + c=ord(p4[2])-33 + d=ord(p4[3])-33 + e=ord(p4[4])-33 + num=(52200625L*a)+(614125*b)+(7225*c)+(85*d)+e + x,p=divmod(num,256) + x,o=divmod(x,256) + m,n=divmod(x, 256) + if p2==2: app(chr(m)) + elif p2==3: app(chr(m)+chr(n)) + elif p2==4: app(chr(m)+chr(n)+chr(o)) + return ''.join(out) + +# wafdir is needed to parse the command-line arguments or print the version number +wafdir=None # SPECIAL LINE + +def uncompress_wafdir(newdir): + file = open(sys.argv[0], 'rb') + while 1: + line = file.readline() + if not line: + print "This is a stripped-down waf, there is no wafadmin directory available" + print "Please set WAFDIR to a directory containing a directory named wafadmin" + print "Or use the full waf version available freely at http://freehackers.org/~tnagy/bksys.html" + print "\033[91mNo wafadmin: cannot execute anything (error)\033[0m" + sys.exit(1) + line=line.rstrip() + if line=='# ===>BEGIN WOOF<===': + cnt = file.readline() + if not cnt: + print "Corrupted waf (1)" + sys.exit(1) + + line = file.readline().rstrip() + if line!='# ===>END WOOF<===': + print "Corrupted waf (2)" + sys.exit(1) + break + if not cnt: + print "Corrupted waf (3)" + sys.exit(1) + + cnt = decodeAscii85(cnt[1:]) + + # create wafadmin + import shutil + try: shutil.rmtree(newdir) + except OSError: pass + try: os.makedirs(newdir) + except OSError: + print "Could uncompress waf-local into %s"%newdir + print "Please install waf system-wide or move waf in a writeable directory" + sys.exit(1) + + os.chdir(newdir) + file = open('wafadmin.tar.bz2', 'wb') + file.write(cnt) + file.close() + + # now we have the tar file to open + import tarfile + tar = tarfile.open('wafadmin.tar.bz2') + for tarinfo in tar: + tar.extract(tarinfo) + tar.close() + + # cleanup the tarfile and chdir to the previous directory + os.chmod('wafadmin', 0755) + os.chmod('wafadmin'+os.sep+'Tools', 0755) + os.unlink('wafadmin.tar.bz2') + os.chdir(cwd) + + global wafdir + wafdir = newdir + +def try_wafdir(dir): + global wafdir + if wafdir: return + try: + os.stat(os.path.join(dir, 'wafadmin')) + wafdir = os.path.abspath(dir) + except OSError: + pass + +def find_wafadmin(): + global wafdir + name = sys.argv[0] + + # wafadmin may be in $WAFDIR (developers) + if 'WAFDIR' in os.environ: + try_wafdir(os.environ['WAFDIR']) + if wafdir: return + + # waf-light is a special beast + if name[-5:] == 'light': + try_wafdir(os.path.dirname(os.path.abspath(name))) + if wafdir: return + print "\033[91mwaf-light in use, wafadmin not found -> export WAFDIR=/folder\033[0m" + sys.exit(1) + + if not wafdir: + dir = "/lib/waf-%s-%s/" % (VERSION, REVISION) + for i in [INSTALL, '/usr', '/usr/local', '/opt']: + try_wafdir(i+dir) + if wafdir: return + + # remove $HOME/.waf-version if asked to + if wafdir: + if "--nocache" in sys.argv: + import shutil + print "removing the local wafdir", wafdir + try: shutil.rmtree(wafdir) + except OSError: pass + try: os.stat(wafdir) + except OSError: wafdir=None + + if wafdir: return + + # look in the directory containing waf + if sys.platform == 'win32': s='waf-%s-%s' + else: s='.waf-%s-%s' + dir = os.path.join(os.path.dirname(os.path.abspath(name)), s % (VERSION, REVISION)) + try_wafdir(dir) + if wafdir: return + + # not found, uncompress + wafdir = dir + uncompress_wafdir(dir) + +# run the test +find_wafadmin() +if "-vv" in sys.argv: print "wafdir is ", wafdir + +# Update sys.path and import our modules +wafadmindir = os.path.join(wafdir, 'wafadmin') +tooldir = os.path.join(wafadmindir, 'Tools') +sys.path = [wafadmindir, tooldir] + sys.path + +import Options, Params, Utils +from Params import fatal, warning + +# Set the directory containing the tools +Params.g_tooldir = [tooldir] +Params.g_cwd_launch = cwd + +if Params.g_version != VERSION: + fatal('version mismatch waf %s <-> wafadmin %s (wafdir %s)' % (VERSION, Params.g_version, wafdir)) + +# some command-line options can be processed immediately +if '--version' in sys.argv: + opt_obj = Options.Handler() + opt_obj.parse_args() + sys.exit(0) + +# now find the wscript file +msg1 = 'Waf: *** Nothing to do! Please run waf from a directory containing a file named "wscript"' + +# Some people want to configure their projects gcc-style: +# mkdir build && cd build && ../waf configure && ../waf +# check that this is really what is wanted +build_dir_override = None +candidate = None + +lst = os.listdir(cwd) +xml = 0 +#check if a wscript or a wscript_xml file is in current directory +if (not 'wscript' in lst) and (not 'wscript_xml' in lst): + if 'configure' in sys.argv: + #set the build directory with the current directory + build_dir_override = cwd + if 'wscript_build' in lst: + #try to find the wscript root + candidate = cwd +else: + #wscript or wscript_xml is in current directory, use this directory as candidate + candidate = cwd + +try: + #check the following dirs for wscript or wscript_xml + search_for_candidate = True + if not candidate: + #check first the calldir if there is wscript or wscript_xml + #for example: /usr/src/configure the calldir would be /usr/src + calldir = os.path.abspath(os.path.dirname(sys.argv[0])) + lst_calldir = os.listdir(calldir) + if 'wscript' in lst_calldir: + candidate = calldir + search_for_candidate = False + if 'wscript_xml' in lst_calldir: + candidate = calldir + xml = 1 + search_for_candidate = False + if "--make-waf" in sys.argv and candidate: + search_for_candidate = False + + #check all directories above current dir for wscript or wscript_xml if still not found + while search_for_candidate: + if len(cwd) <= 3: + break # stop at / or c: + dirlst = os.listdir(cwd) + if 'wscript' in dirlst: + candidate = cwd + xml = 0 + if 'wscript_xml' in dirlst: + candidate = cwd + xml = 1 + break + if 'configure' in sys.argv and candidate: + break + if Params.g_lockfile in dirlst: + break + cwd = cwd[:cwd.rfind(os.sep)] # climb up +except: + fatal(msg1) + +if not candidate: + # check if the user only wanted to display the help + if '-h' in sys.argv or '--help' in sys.argv: + warning('No wscript file found: the help message may be incomplete') + opt_obj = Options.Handler() + opt_obj.parse_args() + sys.exit(0) + else: + fatal(msg1) + +# We have found wscript, but there is no guarantee that it is valid +os.chdir(candidate) + +# xml -> jump to the parser +if xml: + from XMLScripting import compile + compile(candidate+os.sep+'wscript_xml') +else: + # define the main module containing the functions init, shutdown, .. + Utils.set_main_module(os.path.join(candidate, 'wscript')) + +if build_dir_override: + try: + # test if user has set the blddir in wscript. + blddir = Utils.g_module.blddir + msg = 'Overriding build directory %s with %s' % (blddir, build_dir_override) + Params.niceprint(msg, 'WARNING', 'waf') + except: + pass + Utils.g_module.blddir = build_dir_override + +# fetch the custom command-line options recursively and in a procedural way +opt_obj = Options.Handler() +opt_obj.sub_options('') # will look in wscript +opt_obj.parse_args() + +# use the parser results +if Params.g_commands['dist']: + # try to use the user-defined dist function first, fallback to the waf scheme + try: + Utils.g_module.dist() + sys.exit(0) + except AttributeError: + pass + try: appname = Utils.g_module.APPNAME + except: appname = 'noname' + + try: + get_version = Utils.g_module.get_version + except AttributeError: + try: + version = Utils.g_module.VERSION + except AttributeError: + version = '1.0' + else: + version = get_version() + + from Scripting import Dist + Dist(appname, version) + sys.exit(0) +elif Params.g_commands['distclean']: + # try to use the user-defined distclean first, fallback to the waf scheme + try: + Utils.g_module.distclean() + sys.exit(0) + except AttributeError: + pass + from Scripting import DistClean + DistClean() + sys.exit(0) + +try: Utils.g_module.init() +except AttributeError: pass + +import Scripting +try: Scripting.Main() +except KeyboardInterrupt: Params.fatal('build interrupted') +#import hotshot +#prof=hotshot.Profile("/tmp/proftest.txt") +#prof.runcall(Scripting.Main) +#prof.close() +# ===>BEGIN WOOF<=== +#6<\%_0gSqh;d#""QRi4MNW9%Ys8;Tjs8W-!s8W-!s8W*A!!!?/+s:iT#QPE@+:JNalM^0u7E&7,zzU5BWbz!oq"M1#9$/UR'Q@h2E0$HX4Xf:2NWkAtYU?>A0Z5XLk)fl$_aFmsXWhU_,Lb16eF=;LtY_;4@7:GM-3XH_GC2j>#5d`0]WUYfGLm@uTQ\8/5QGJZ:^!al1hiXX4Z1]3!!bahTQJ6W5j?(>6<:;j),Tb;_rq(S"]4Tp^n;;I($KLb"*XME*kPRs*a8a2mA3[.1[C%gSaknoQEB!KJ-V"e78sKLqQ7f<[g/oQpRq#,Vr%IVd/!*kI"26_!32kCX@/ja$DLgq@fQK/-iaSS!!!!";/3L+=0I&?f7!+ek.8]W)APb35bL",pV+k_pMdk@?LdFY3?)DUUjT#DH7:>M`,MT5>M/qVQ)JWr&9p34.AoCg76^f"/EJ?[LOSc!G2QpN!kMlQ5PB>Qe9%Xo[YKlb;dNP`]gkf@ssK;9s,dbO;"NT3MhD^$AIlhY2:7flm,"G4"[>Y<&9$D@`gThd^jnZ_MkF;8&MfhT_KQmgJI5!mpgHlFYZOZqC'b^!'!4K5Y]WkSeBG<@%rN>Lj:QF;+aoG>u01g@k+Y`YU:#VlpC;Vn7Acp27T:*mH>2S\UodkG^QSWW;eD-_iLEpYGS9D;0"0)e=XTS%GZeH+R"qG6Vro=)?^%<8/(-0o!<.!:A;%gu=t9pWoEpgV1/+h`#2J>>)ZQbs<_*Sb@9_n8e[!qoO3!";6&f\`o3cnr@F4mWY5$95Q*D:)19M*#1*/qjW`(cF0ohH"Sbcj?;r*gO8fKJ^K*SY2=G/[tW[F]b3d#aR,7p%`R)?hV@.e\_[;n;U_NV[bIcYT6Hej%op:XeGJR,RIVId78h^iJ?PM]q?Dl)pH7hk*r!DC]u.OU?Z#Pf^%]/-]_@H?DVG!CXk<*KmsE[D2n-r&Z1j9lpNOa;T\4qGD4TF@T(]d&k69`8a5+JD-#2Z4^"r*:3\V7INr!(i71t/D!e`=D!/W),8%`Cj]6isn]cM^NFjX2S"V3l^mpg\RYT3@:>!Dht^,jl`_Y8=.c(64"dKXeQn`/[l`pC0`([UDk<"N=94JdUXD?tIB\s`!.]Sb+:)W`d$OrL)?aTBR8Mp5ksK.e7R<"V!!!.T+p``HN<'#R!hZ;9rDPPH]i3T2RSQjX;ot`-%0K>!$%8JQ,isPJ`I(_*2iqB_BAV?,)f%m'T+R?Tj/]O&cNVHHmAUBFJ)M[a@.lm9LU2t3gM1h8cL-RdLdWh<NV%3lH^70+kPbWurM"Ubm[_%@s3!@.eH,m6RM"VVRa^dO;B$l)I@,sagc'i`+J`=Pu]:dHonP`-:j;\9^#6Ua?t6:1ke6pUhb`(<<^&Oa^J6U=+,&JA%GKG$]V;$96["9hK+PT^Tk'ZEL0:t%,Bd"KV2727LS9T9TK<#$[p6ps?V1pg.SYZ4)U'N!1G,r66gKMtRd&i?DG_[I6t@aV8J*f0;^;oNPXJ,8$OmeH`.LTS$PQFW'*S[TPS$lG1GVNq92)0U5tOCB&/S(*oi7$%aW>)(HP\#`'glVCAL=Lo*WZL`g5_!\.rR$5S/DTI(jJR3Dqe8j]CgTS??rPaW^cP(K?nq*K'M784CL1BLQ5'L9T09b`^(9%>XAN=,k69/6Z&#f+U15nGda*A@dR3T,"&fGR\_J>alb5_=\9TdlM*#RDb'Qt`9#,bc-=Q4aHM#RQb>A-N@?)G-f7M1UuV!Z`n*%5B:c1C410"Xt]*`"Fb'OAmr4&P5fPN>4-=1/ftp:st:g'bF3`9%-*].7l!cK#7Wn1lWma5SOIN.tp8U!(\m["%a4>4cCp)`'I5)+\PN[,qpVh:_=Br#n2do8u!l`&OeDg70Oo&_$lM@0bh!Jb;,.hKeP8=,SJIWb"DfWf`6V'l_!>8(&YfTiV6j45Y#gB+Q6Hl+@!DQCq+B(iu[$7C3:f,)!1DEeU8PE)0;)1g5d8`an:t&E:9LCTIb(Tsh;cQ7";af![<'jKI`6cq16NES)/4hBd&d9]mZk5"AZOp2;";*CeYYtfAV(E!A;O%BI"N7QB6kGP.'b]"-;_T@\&O?Cb`!e%.'NHA(&-RYTBR-JLDjpK*FW;8kKekTV@R9-A"Q.+:;MXUhRJYZPnOX2E=9M6!19#/p[&@0Nt<00BY`.V)`_6=-:W>[+\@:C(J-kqMo-n6*$JV#'q6:=-W'SZ`U.7?e$U4SgX-NSQW&HY?H,r#uf.8rYZ0b6m5Q!t)d;Q-t>s-j*dWs,EdI]%Cr(m4d>e0kLPcTrcnGMfkMIt2d@80uNa&N5E/mY[Y2cPmY^KAApchTGs,*%kFc2X$UU,l\(FVC$6&p?t[,3r=>mbD$@qt)SQhRbC8\U2["2n>ia:T<6)6H\9_%A<;W*o6.j_H>H$U++PXZ*lD;&Gm4dQPuVA;ap3,k@Fh8'`led1@no?to3''K0\[i@#_)("eY9d).6dmEWFZUA-n&]8pH/)rP:S^McNds_PS9gslAVP%@r!r4/Nj>T.T$;d6DZL#122"e$EZh\m)[1PrHDp?=E7-lps8;>TGnJ[Wn,hQC"7;Fup4f-9;m:c=^iS-g^"nA+4TN9k]B>+;h:mbT1Dern<)TjE2tMkMN6FSFp?FUXP,Oi'blRY;Pr*FG9N_5#c7X7K3!a.10c:$qH]4`TcKcY3^\$a>W@apXq5K)]/Yq>h\7Lcos_R&ES?Gu)UG&1P,Pua+r$;-EOWWCDcB]V;!@/-eF?MjK;^@2(k8R5U0JsbsjdqQrm7.E4UBap7PipE&\MlAk`TpX!=M.]U3*;WX`KW.PI*RBs_$l.D?2Ru*?Zut73*Z!R=$uq+;^g4Fq.flXjcb_C:NBJJ%O"SWZ_2ra:ZCOX26]pEG(UZ-jAmC%I,@3?I]0+8b:IQ8"VPinq+oFEB&:W#a8J!CAeQq.E0J+]jQ'/NlIe9P_Iobr9@rP3][1+mXd\ro%lE=(,Ge,sBi7KpOJUhV)cI5K<*@R`fDI&KlISf?'g&F([mdHjS8-6f#OOLIQ1q,N0sDWW_e5Ia[AW?ajb$>n$bl?0V"Ves4qEp?WQ-M^e&4_hG+Wqg>)">&%pVrZ7,mR=\\N=i@;:.c271V*-KB911'qp"b%\S9Xla^b*F4bBTARI2CUDD&;\62U/Gs;om+GIc=b%.T%/p8B-p2Io5P[=U*^Nd6`98;Eq[qY@p/b11o$L;j;j.7i=dq_i"[pB?)5Rg=*4GbpIbc?>J\An0g<*'jucP.`T<;?SHRI]9d]n8rS@!9]B8rUiHI+*2h-SJh`:Ja1L+RKLc"'5ZtDM>WKoEX-f2runUHIr;Mj>``QkAa6>:2qhp_/ku-*6`e);E7SsqA?F6,(*>B+H[a\LOrjEoPATdEcsp0!n+&H>0_d&<+k]^(#_`NnenMEr"7^.Zm]s6=Yi_,qrDa?12SG!4-AGc[0^r$\+/u!E[tLpfM5QElOD`7CZT_0EU+gER`6n8*X\Z+D.a:)?@Q&oG+Cs[$1s(n<7s3E[D-Ej$^@ROB8LE;6%ghsB\l*geF/IsQZ5MhAEoY1l:H@7WXPt:l_[T/CLMcRG3dU?i=!N_MNY[u#D0Itrd#of4S!PC,l.Aj,:%9J%Qu3Xb7Nd,fj?a<.`M&E_a9_C)?^J;j2toZs7psO,.0iFY9I3:4h,9A54MOu&#.;*:)%E%F"69,E4NgNf7e;!/J3!Cp"YXrQ;tEkj^b;_bB_7=G\SC$Oj510U8<-)\ii&L]?6NKZ;,+iM5J$>.6=/#M.l'Y\ojV][<.jQk=$XiiTaAoBs]/LdD8>i,djHX]^GVqdnKMN*=6BE22P"PUm6;`^kmIsP(p+XP!k(k+1aoh.*^Y3DWo+ICLYXfYG8nJ,i:Ur*MWM)#d\3EkuJqdm9rjk:HNM`\5DKV-d"6PM:2$N>G`bg,ipeO'M#cgWTG)e.he[;rD0;ZG'%B;(lK68?nmVno-"Mc&(=\.@N(iu;4Y"C0Hi@f$q;0%$5eL#7Nr+I3N?g.,(qY0&EE?GDP[4^G3Mr1ZkB!Jmf^%TFR'=jhr2=ae&D6+UD*"cUpK/Tm0*4Kd%(L?r@=>,bIaWpEYjB+N+9&PPVHKkcNLKQ''Fq_-;V3_&St\$>%)@>B`%cV%/=VWo=9OFa'jd=31[5`gRLNMP.FCab0#'p*%3M,^tCge$9`6)=MPim4Yn'm@f#:YSXa(3qYmgeFBO`Q)(*Uj94uf6h,]F`U\F,](F]g^%:W>f:)BA&omm"e2opoIR,pj.7#T:jmpo&c6#6PJTjdd_]l;AcSPfHU`+GPA)RX!*5nqa2)=`uL]FaAuD^)ej4QpW_g-s$9j6DJ`mHfWJ:NCWgAiV[@b-OZf3N]I]#dn7"VU*ge8)=ubX4'.l8Dh>)5?g3CgcWak-+N4ut0\'5F\n[n;=rLCt.Bj:Q)R3r"pqFsL[jpSPM]qC@:oF;Nh:G%VfO^T$;[lQlI6CZijeOj^*hDFCHm-QAKP,0O]Vcb=@shiAjH];;VLN22Ylf)$N>"c&8Ia&/OFPe"^X8,$H-[lKq>&'qHQi?.G"isQHmEVWNj)63HS_"emH1^V!Ff0O3U'G#LIold=)nh+.+e%7;Z#D`"G`o(oG"Y&mX%_er#o?.>]Ps)&j`t]OPSco>!4W%Y4O0%e:pRE?s73D(-N;+;E`j*'i\>MG'nVYOj]$1B@/`G6:CdV'##ch6=t:Q6ZpLDiI'l75)VD*JtA7dgt#iPH+nAdOm747O+''ffiRQr?,G5/>te>m``6`hS)8\k'Ih/_<%>-]ic*+,D&U?&r=XUr+eHCT.t\%'jf]C>Oac1P([3_Q2,`f3"ON<&OQLP%l92em?nS^8kH@TaHn))0Iu5o^Qfj?^]19/>d]n04?dl&+\3[@k,YN)?4HRORrtSU`Qh"7UDNg;)WWJ2%,k@3P?hcXY.0NNl/1t9P1BIQLbaY.AXWC=[dLNAH4%qttMUTr(XG7WLPhtch"0i"D)SWp#t3:(u0(X`'aW#'(3/Wg[A28ilg9@(=o1Fbg+,)U2!rAl;UI,WG%IiO9S_b8rAM\O.^(WUD^kZF1K4OIkAO*B7Hpq.\9Tq5F;jc0fmJbf^BK+VW=*$5:16sF)PTuq9#CXfnt-6$RLNi)St`i646-sJD`eX&W/bdVC0p5]:-hRG>ja'][NK#FP#7f-%^[X^4(JKWO9_<7J2m$k!-W',Z=8INZ2ViiiJ\nL9*M!l7mT,["#r(9@[=:\[QEPh0et1MrT26]*6G#Ai7P=h':M\.beY@#SK]5Gs&>]dTe5G.VA7$\iZ[u>30uKJgbAOV*Y3oBUI7;K)h^B>6krqe>N.cX&^[_'7pVnCZ?EP,97k;9NKMq;imN[iJk8@rYgDjW?"Vo&WFt_,k^aUeSeOmtgGrR'TZYre:2Se.MNZ_IGNV!iK[&[_)13fpP.&c6SO,&$c!k<&4BFNUMre7X3-b>[TY"t$_T^Qd1"Y*L1S#)ejVH;mX@OV4+QTuG>KP97g]J&,+^lHJA%MM,E])g#1lm%`,?gi\A*t#XMW:>D=7Ql-af%)(FRXUY2FGttFNQ^CQSPehEEja97>,oAG!j[+-4O#koI?ZG`*\^(7@G99n>;82RpJ8QUrqN>VkdqfdnJjNK&k[!QgefJcohejJU9jE88;1#QDE:>JX(M[D]'6))KV`O&,5YPgV%*@^RH1,bngg]6egQhZPIqp^8+3"rN3)/B4&%VZ:L'@6rVPlt>Is^!l:J17Ng63b+]Ymj_d3q7/4?+;Y2o96jq0)@3,%JDUu-*+N+@[i>o,n;B\FIQXJSXcKbIq*ie,%T,D3TqcBD&8?Q-6AS:%[`:iFs=`mYW2tV@#[Zjo+lN]?^,i>]LPNKB!3;]+A\>I%+r0,+GN-W=IJ"[-9TQ^XDZ/j^tcF6q/`h?V0G\Eo^.Er%7(_Qtk=E2[CNS%b7VX-8S&J$V7J(-Jk]K\Yhqh0OPKb"eF1R^6SMh03S%6O3M?I*V=WCE"as&8(HT)X,X%-l0h+:1QZeL0nXe]d*2/OFNCCSO'Tfln1pua`,QJ4K7WZdsH*rf#qr#-3,%lDQ;6:m[19`DM8ceic+h`/TIKjIsJU;Oo6"Yp#@B?AK9JWGV(+*8Su1p\Lc,Bs!2[@o%l$7/Oga4QGb:We]tX1QGsP%5b$/EZ$T2G:%o+-U'IWqs`EAqo^m:X$fd0N1:B=K^3*qU8J\SP0!cb[4<-N&#@HP]2Ad/6&OL_iCb`[7(#:.C$+U&Z]WPpXE0>ReV#:9Hp24R'00t@"T,7fp?D$VN>7bNN+b2+m&KBs3%kB`J%&h>Y!uf2RHZ<.HG.l&rJsPq7rPa?O87$rL758Pcto+d2mttZE:HPH.^>QT)/D6h`_a2b?ZG6g^NAgkM&LOfem1eapThZTasqu#,p.PsQd#7&rL.Q8F?l-1=?EQbbC<&V5U'\YB#OOt=PVgKC%3,uJ2?i#:gpM.sWu8G]7DBB=W_g[+-@DsZ?JXKSt,VDM9uOFKNG4hj_Uk)qm/)?(^1^O@-2gZ)3m=ms:NK9=2NSZXBACE;cFaUM9c>,XPX*N"t1opE!>-j]EQj4ebbU7m;U_b7.AFg*HQk<$Mm!OV;C":=`mW&BBoGCD3*7TG;fTJGM%6luj3@U-/arFEJU%2;K9(4P[,R7Y_+,#^]H\>V:(i^)68+GADr9jJLi`[eK\N9Y/..D^p(cjs"^)cY+i6.JR4]%>i;j486-H-Rm=S._@RofVTTR(j0\468Z3)_Bk8cJsLcMKb8%\Sg?S9K.gMg@o]D7?a=J%OR>5i\$76F1F,f]sb2h/&>-*P9H5,S_:%r(><:N"o7hVjU_(#>]5D5!JJ5s$5r+%I;fH,h8.Wu]0tU$S.no2[_e=b]\]M^;/QNA`0kajBS3=)*Cu;FPEI1E/h[K%e#=F_":?Z7`mdik+V+%J=n5o8)a-+G?#3q,JSU7PJNodk.3LhTe^03eM^bM8Iq?'q6li0g!q^B?LiESl+XN1K'lIQ!sOdWq9Oe`TEpe_LI8DK$'_#^u#5>T6F5$uVFU)to&B'+J1MbK0KsWsmG4_5l[oe\\[i05js0MV)J%P",rddSe['+>Acn3pMZ10i6LS1o1pX1t";5b53PFfeNf$Jf\$snpbU@Fgq\uXj[>,G$o6e9^!<"GdAE)c%0oE",>RY:V2^4,kLAGVfD<(WR"S.0\OHAgl4+\)Oe3.9-$+i#Dr9fUZ(%cR?(KE]d8BAP7?URE,j8lE.l2?S.\)^MZC0W)6-m@LYppGau?8TBE8fiJh,YG7!G^R-8R-YbSNVF(Ijh-1!o]7F1%VDO%DRhrQ!\8=-L&lSUCBqj62*.I!NdYO'sMkL9\G(`-r\qRq;sV^fS%HNC"(W\gFCe,Im4N*Stu:;\jqu9G$togb>^hgI*HP5P=hl7X/T1Tqe*8\5tq7A-_*X>tUI\r`P#cNUrQD-W9__O6VrFlJlaCE1eZ[c[T:r0qp.I_]n!WPoDPn.@g]%;oFY7#Y!B`WHh6!@pQH5)3>qr@N-$*l6=A>tGRt\[J`t\QQ7,D,7a$(JC!f;pF(gN_M)-]*mWVgjH`P-uk+S?$+TKUlVlLXXr%EJLNo#\/2FE%s4VCq"nlqMCMTCco)3;^R01TJ-5jb$YL\B"%7C4,2N;af;fuU(\EKO\aM_$?0Qne_WFsCWip`!7%Lpk7RG'<:][*P6Z+TZfl@50W9d.Eb.Bct/KpkPNiKS-UeTdXTK,nKoZkFOnnB^B/]M)Jj\T<7i(5[rSd5Za%r(4a=C5X@Zq<2+Zb8jf%d5e'6(R=p\R7j:WoH?;!4P?W<:+)qcG#+@@R,__pPk1I01beu(?'r3[jMfKhpkZ]P`1dKLpR2HNQ4Q(;NS63/r9aU9t=J;A%+-14,CG_foPc8Q,Y/8#^.t$-`C6;RiKC"1G+?P^9g.3d1Kk#u",o9g3S8F,[7VJT`8=6#M5bDEW9]H72kh'`:/W<5.*Cc;k&i'P-QZH4:E]/AS8V."WQGfu"LFj\h)Lh,Q1(a>"3S]NL5480+deOHZL.-QXGM%OJ#KmFq4\;qTl$Uf@i<2>8hUXVapGq4XaaD7c@9E6$pS.m#gU)97>>&F]+<.;Y;5l>4BRR:.&R$8ha.rHt-SMVs9)p7K70X=W^Fj-*qn)Q224_X6HDO*;X*?Bg!cans-A4n2kmimZeG#+KQbrSk7R%.NU1g)>Wjs8Ji\eJt2^d+oTs&+VF*$'1@s6lmC3'Ip8V7V23N6W>jE0bXN[QbK:_,0K%-&NR9ao9AR&b[)OnOK)UPCLJ:i43mjsL1YQPe$,kMjOJfEh?hfHH>&0?8(dg%NGc'YWkMjR=gfs97VK)AJa!.>MSGQgZ;p]I4G4]d+T&/b&LrmB,(HI\LQi1:+*Vsf6H,oZI8TU$hjWg1kZ6l>$3h()lY'SUGhp/)D5_l_":J+8,L&<+YQ)-PY4*c9uZ;:371=8MZT\JlKSZE:XE,n)`VoK$D^ol(Qa2;6_StCbg?On-:a6jLt=1K=E5JST8%th3+S)O8+mqd1g+8,-slt%mPRN&*b3k/%J2u5D0m$I&4)4gl#FMA-Rm+Qn:`/LEO8X02fi6b.Ul$os\9j/RHTbAJFoMHu59#3n6HWkg,<9O(3)6`A0oSbSN1D2rO92JUcCe6*M"@Q*Z9iOYL'+mK,5nHRb&A;Lo+)eKu_?$:'"rbXkmYMjF17cIM*+U4cWETObfNso?k5rYOaF<6_a@bsDX,i$*4E+b3)Gqnue'&lc?P)5[)XTkGZ1]c!#VFY_oBok2C-ECa(Hm39tCW"qQr;+D[d(@qd5GoT(Q=NY5!^1e>1-lc!6u6hn8sk7_fD-Moq`>;FX7K=&6!.GrIme0[_LRdCHp$llo@PkcG7t7GLT7+tSc503pX>>[oT;*g)Sj#D&YHNY%boC!K2F<$=*ac@HT7D^i]i/;SP5E"$BtW>dS*3!'*E6NEfd=2@Mo/oQkb`5$*f?IX3r^'#YlPc4CVL9_EL2Y;'LjmdP>F*d7L=;-SHs?r?B4&h=VL!fZc,_WE[;7%.:r02MV#'s>G*]Y,`iC]_7>::%2g8,?55o\^cC*gm=l6?]An*F?78^a+[(O:Jpm+gdFWlGk#2Nas/!@_[6D!"(n.`2`Uq?J"1)F$d2)8Q-ok(sF1?+cKE*;Q(23Wa1Z??jl6B#a0gbk'>u%`7jZOG!S$`jK5Lh$i)Skk1*gqS.4%"@T>ik1>*=Q\e$F!`K-+b0E)pAo8FUVcA^`"raY\O[)rj2&$N<]0F<5J,N8TQTrTaKiQJSt6g=^,+M;,`GEC&U*BZ]V%<-`\?1J+%3:Jf^N[G^Ct]JauD)E1]biG$mdIDeCWd'WS5Q?&/M3sk-tT/cJDi+k/e4(`Qk4NWA1,dgN\7JUVCPjhXmMe*i!&De:C#qgd+pXP;b0O5,L+!TC\dn9Lg)FOrak3q6doFHnuVqC:5`!AaXF%%KDJ6*ul;jS2\md/?!STCn*N=#CL\QOiOC7I'%Z]X@+_*sc.W(uEGIQ]>ICSj2e":<;L`EPg%(,NloR7Z]H3GW?j#3RRRP%QI^N#>,YXR+,AHR1]LrIJYZL2]D/aC29>S0qGn>&1:32-s`gQecjF,)fGpYpVGgWTQ&tcDji:Mo8P[-$*7FSg`#;aLntt?)j?N9@>.p3aUHI^BJq9Qs&n.!:fr^Z;Pr7W>*-H@d7RJY,/G:DeRdho3VZTn'm`LTU#IUA9FA0%/`niq)9LCZjoX>0DdM6dta`eYo-#^G?$65IZ*Jrn6?3Ap.RTDZeI=E?91&Qp$[A65g&/I.eXPifFL2.GmomV*!^tBD>WJdVf!e:)'Li6C`T8#H[`P(AC/gd"c#BXR0*(We_cr)ShVR%uEh!5O0Hj=-*[kS_"(.\/mGBI6c12kd0Ql:%8L\i_tYK57ma6iM(fCDG1N+6YNbOm%]-!`aFC3nO.RrK\(.'r6Z9[gNB:YDj7DDpcYm5%$9\kA3+h'k@X[CoB:8(Gp?;i'K32bc!%Ktf``gd3bG]S9stE/YtENOYHQ))fsf@fAb5PnA[tEG^=B$_l&^_`-paf([W7DCQ4lV2Os%7k!Y\m2:dH22r-)ZH",H'sC7f7b5G=hU"NXR2!]Bgq?.]ADpkB@M,tgFnG^rt*]\@[G_I.b/G8K-*.W&,>..HJg+S4r@E_GD=VkcK%M;6VmAd0m*8.c'"h\WkScM5#O-ES85mSUp>KLb+;6QmhNerAH#T#.\Ig[DA[kCFmN$c\7[)(+3;M1i#E+e.AY6fF=YM)#Q13;d>)_lg`Z$o.RS%ZHd>-CFnHWM@C\\4fo&\T.]Qo,`Xt`%C=7Q56l$gVrQ"*E,@H`>Mo:p_Hn(QIX-4,/d(3;mp4n'ldmDNUbA(?q+oj71?9pTPH@Fl$R:*Oa_>]i'0s%H@7&W%kh1(dL`4JbJWb[e7k\ja=ad21F&&H3K'$2nd`i;G3cf4Y8CfG;C#^*bIQ'YR;/`Y$bA@WX.Q4E8eeN9tDb3bE^:@g!,6eUUS7fL@8f+-)mXm[b8AGj2DM8n@-mDlAoH-cdhc_UJV:ig9M@2%qsO]YSAX:m[qeQ%`#L/"t-!i4ksZQFBI&](2W?5FjsM?r(h@`E1<7X<--\[`QbPI(G!3"?5WZd#Rob_FFc7h1ZE2)]MTgdu4mB15l!qiU4c[4(%qB9$9)i_;aWb^q2pZKXQC[[kcA0bngD1.F$k&RN4PATb8tKV6R^C/^c?T0ZXlX6=]"oKQ)RirrP[EgAQfkgV+G"f3'%B/#or9t8/NYE;/jaZ$Np?DF6SCb+T].S,/9ouM,T\"'afDhYB-&&aofplep8TOa]u`r)a8$]>og`rm#bM,Tp/=#+Bu.MXZ([4a.6Qo6s;8Ws1DYH/A\mW,@scs7<$RRss]S8skg@e[=^[S#uYN?0:EC1fi(.(A8a%:W'X6Lt6[cKU^Ur^^J^VRstld?c5J#?;,.cY9fs&f]CULa[lfcl.fgjBO-sD2Ui^6%WXm[#>F8>/7'XOjCW.F9:^c3Z`GqTpPc[7KT&8mqQ:)iq:XlY/hh.[Y"sa,bEXNUMhpIS^SqslQZ307-Yd!Ydah$t9PVderp%SHZ=^ei'-tK'K,ltj^7=Hn9XF*:TgiOhHFpi788*D?AWZAA_^;@@D%^=.7`bt"9hrAGWfE^mW7>"^%[]8_H%!?t'd-)=Y`jF(E:kU&lO^URaq89em@QANFUlnDQDqpLpi=TV)9R(W@T9nd.#q0uF)G[A(P`0NM3H@r%a-p'V)p,M4ph9&e)cD-elb=l]"6!jHONpkl6T?[eEegg#eH':/<9)>PR%)Y$=lQc>,^deJO`Hr[^'b*U3op]ouQ#'OV[S;nkVK94YZWM$u!2P8X*SEt8Xd@UpP<`:Z7jBIOjm^jpF.fboW^uEG(c;N7l]&7CC*,IqKmB,geP.8J+c>kTZ>6AK&;m1]fXaG=hU0:3JkWuPG7)d5^TG%HC>`%hZf.\)@`]krAGVBom(/DHaI71=L/Wq,Ve9OXj+)mCJ?,gG$u0/QhK]]IZ+]q2sq*,stAh2_BCI+]]lH>qVdbA^pr&Mqkiid+^U9;pY,>V58Jme*p)JAX<#$1"$QV(d34*FU7ma!k[p=kJt@+.Z%Nuo96[CoUg*[b:I;]"cJ??C?MVU[E5luV#oOSrJ=<9'P4IWAoAHRbn^f4$E[ikZWkgH29a\&'@`==#B0a;egIM8(4be'>\7g\lI$5Ic/iS,UVGLgePmCEd4r\X6G>;^ZKGs%SlRuVr4Mn_>ZlEaW1pWW;AY)l9"Zt9.Hh83bSdYGc(4W@;dhY]d^P>ug+12IP4Q'[j"^)cQc30leUrp#?0G,EaO9\&NC76OS"jeVkL2r<;bD="u*].`4hS+5Ntc_m(UD5d82JPH)amkr^rNTm*UB+>X^QYg^XiXbU8lR:`1SFN26`0RO5Via4d].rnXSLW_];!sMR3gUQWCj9$Iq53+S(Qf/ic5F\(4^&a0B-6kQNYIT8ENK7#B>!>Sgf)Chl'g^?3qqV&dVFsTY"5+k712GaG/1.[SVMmiLcYrA.F=1ejU]jbOU.@VC5uJ+Ec*?lY?3;!E=opG5s4bL.eQuh2T]!eoI5cH\pCu7_oG>j6Cl"7C).8Y5&_Ep5NnALc"LUAJXSd&e%/;61/WE=j#T(cC5]r#-)Vr?AR&h-Vs.C.&Q:Q:.Z\jug!`XM-:oJGTqKi6T8`$bc@5(=9oYS02\*@OU:YG`4ref=Vi^+/>koQp'7n8o&!lU@e>4Oj,oPR(Qoa%L8;*9jEh&V(ghbp97diCl,2?Q7;4_MNV:04CR[FOrGb>l-UqkKtts>1c[aC)LO&RG@Xs^?B)KKUue$#h1"deL'P?+lYN-@UQl;>7GGA<,B*;);LR\N*1'd@3jh?fUF0fk(Y6Uk`qU%!QJ#1f[OcM39WOCO-?1?Np`VrODJ>EUI*d%,,21oFdmh?Ch/96]ig_1u_mQ\nCh1[u.`V3d]Yl<[ggQ&cL@R*[>EdIXBskiVOrE&+hpXQVQ&,k6Zq)i*E]$NRT,D>J>?.Ni:#kVH=0$9d;-(BPHAn?#h%L4K9HI.5jYs=Fn9rc+o9u@%8g1"q,!4]2Arg1"W$/$8gdZ-.hF(gTotS5*+fc<<1UuLX8rADh8!/d8Uo?4oIbbts/hBps`QEt?c0`IZ0@]+0d@W)R2kt#XI8(VYo/sZnViH"c>Y-b^KW?t#KQc21k;d>lBY:sI]EG8`ohDV&qX.pd+/96iQaG#.nEp-n[SunsGN`"W_"o3%6AI!A$UiY+#0:[Af_p>Z(Cg6-6@$%83[Y#d3?1[HC>(oq@TqeNWIAbGsWBI_&ULlic4)h'F,@*+>j[REVF/)K_,W:&C1Rbn0lA4[n^h0<82aOb%d&At)G^5RTK2*Cr47ghUc`[fI9rn]>5C,#k@qhBf7TMK[3UD^fEH]"P;KjjMathq<15/U]5/toJ>r/G2M3G<+g\XctYGaM`LII$g_JFbB,t#nuV=Vk_cr1D^NOY^KBsOtN(Wk)04&EK7cR3HcpGd;48ss5e/h*[5A7@:;b$=+CB\c>'Yr^/el>+^PgpPSSI]>=2,X2I;mAS&T#;0qZgC/2]4uK$#rS#PLT+:3D,$#9C?%uf(CTRd2p@<'tPM^5H6piIsq0i>B%K^C,A6JXPiU"=B+m"p]d'>bLc!$1<-78.7bf,;`.m;C@RB0/`6LK5r5/eoP:pdKslE-CIH[L@/r1.P@7$7>XY!FdD2Ok8*Kl9f$"%,QsO)4JW'NRgG"Q>'1d/0-6XOJYJkb7('(h,fZsC3*cg16F[BO;hM1![RgeX_t,`m=AD^]Lf9_l0;lJ^S`ohGhkH-7%[[lDPAuDA#ZT8d,t_i"L5UiqIlnDa=>,g\N'VkhG-Bu!'Hn;I8&3&K&OD^(IT.V'6af1X#6jOK@%SGqJJQUCrR9oCo=Is?i2/C!SG'!S#'+eW[#]b_Zr%CK`&rdbAJL[V7qmYh?Vt!=>orEa\#k5EIG975[Z-UaP"]?p,U5)N<=VNEpjLIR+N1JhCLioCc.A1;-oFYrNCA$g#(0m#n0:-%W#lSk>erj;ih2%@kk'HSpS28JPl9o\Ur`CO\hP%^V5[\.$hQ:G'TjcFEL-LuP",<1`ptjGEjd-g%=6WYY/mJn>c@4S\b37!$fC0_XK%nK#>Z35!:B$]#mHXUpGOaftY_rcoB_#"6D=BZPP!D"14Hrq!b;;3.o%b]h%WCsX7_dC1GqX>:Od!__gc^RLJ-!?r+)gp?N,T^2N)/%ZjK:L^sB.l[nrL6da0,CR/kAKlL+V\On>fO4FS[>H>Tho2>'q^\j1mP_[_XEbP7c;rV0q1lS#m5_iFf`t2?5dr4pJ34WK/*4K=RO1o/ogJ,WOD!4848qC<6/qp*FNqYEH&m.oM\'\>iVs%aHVoDE7ELmk(Us\=860HT<#"Ol%W.fGII65.dNA8Uqb=UNh0'8In#=sDcXMdB=YZ\l6,'>3h^1!Eq9J4GgEncUPVp@Qm'?JV$gidZ_c60sE;eK8DV#YYh`mU,<,>9:@0.#AN9k8hI@2&S&,je0X6>aII*4la'X_mB.(E=:A'.G`!5Y*!*a3:!o21ngq6%;?P'Vb#%NoN!9TGt"tXjY$EN.+P&Cp^Q&X?+<)4'Zl"uT*H4.+!2+==8BW9-@mo!)%W(10?D^A`'=+YF^uDQ%MBaa7Bp"V%=n^2gK12&DeXLT9_o[SL/;]f.Da4+#'7&dBDN3A(XEn@QRL;kBfQ4/=#G)7b=.GkXs2LV/]&[)VNYolesf);=tkN-938O@TJK5\B':WsD*\Fm3@[^IUs1b>kl_^'E6&0VX]l=H8m+,3T-SgV]93)OB^:Ctm[mqc$;eEP.eiZ9G=j[2F4E):"S]14#(:*iHn\fJrT'WdfPW'/b#6I^^"AORt/5>T\fPZ0;4_:^!.:FiZrUHj.`,=,\+gP_?iXq!%SK6"?NN=u,>Il\X"]RIA7s6Ap&,r_0n6dTW72+48*IFZceHEf,D*j4Cpl\HSsLB+A`3"[m^ZGn%OFI\$8<`c.XeM'ZQ!h"8)A3?t,7kG,U9tCB\q?jUD?$WQsh"a.^JsZ,E?p%hihs9INO0m,BYD5,La44W`JKdsl;uWF%6,%50LnU.V&rJ1K1F*hFb*bH#@A^-"N8+3?Id/-T-*MSFP*5+Q85_1Dm>S+"nn\L87b_f>+C0M!B?>4e?QYcg+5fQinriFb.dETXVI,$35XDES6[IeoT-fHm#.W&N=059gojM!51C%=2*gp`FK"_*dima804qTj!']eO"s4`&^7Q"Ga>ZiVd\[1o,8sgNdGIR;U>dh`"_$*Z7GY?_HQga^q?i)u!Q>l7b[#M!phcUQ*APQ3N_58Er_o>ucJ1mYpPZBsPgFelc`L0c?0f1/-G8b`GNHQY)QRm'ea'$f`OUm:9<$>W)Zg"QIM1s>.t*t@W#5:X@UV,S[3W9b9p3.GqJG-b8TO*lT3C6<6Mt%Q0GA,hY%:V&odf+Wd3/?&sg#NP%n)uC$TK7'TMlgHEA$D(&$$D0M$UB/K[Y)hLMERCh/L1QNBIIq)N_>H((TP1Kr'@f3JK@EQtScK2a'XXWlPE\3_c6ZEpe8oQ<&n#32L(;H:?@H7qmY-2;si?F?Qt2L0@"?:))@CRW(FQA7CcD2J.-V;OF=K\^&\C85m4q[Tja#Z7DtC.QpXU6+1"a=)D3o".CX.<#\o,VBP>!1q6^tB$iGLpm9(mRN]S,W/ci*7p@dt/c)%'pRM$K_0RV\01K*6d:T;80:!R]k,a"SC#/4JMijMc32iARj2L[h^#Q;"AW`8Miu]SV%:\AcrAU.bKr`Kse@sI2D!*mKkt=W`7#:h^.:BCro.\]ir^IXW5#A;3ji!QdCQ0uP)&lXUMc#Z*4,'uNhA;a9[&8:St;O*gN=klq\qr=LCJZAm3V'GH0\)5/+fmC1cPHn3JiK^,jjiL`L)k?&B"b\79[n>ae'HF,TEQBn_5phT\0)O"mHJ$VAk1?u'(4&)Oo$TkLM,#g^-a%ARk!=>XF5c<;g_aR)/<&LrZ;9tKFiq:1mVt)YZI-8_A@\h%2'UQgkVm@0nX>5"7trS-q>?_!Y",!URjBAWnc6,10X4N2sWT%HT:.,SKKeliqN&1VV'G'0'SkeLOTa`@tn:A0C?r4lnZ_811muKl#ZKeh2"O0R>m/Z,8-$%.%.2ReWVL!%n+)&M^!#JD%1S]-7YJrB@E@g3+tKL9N%]h7IS.7P>R^_GTMp?%*>c2j?^blPifb3=t7l\U:a?pEI'eVO;`p@FsJCgde$<;!M\"AW:-GFU"blFQ)ra-ogSUUug*OY+nF,kr>06D^('d4L\L+rqhf%]/@6Bb837oLE+CS@apX@-D%5KB0"\RrWHG?[Ce(d7VpuFMr3TbMc'9F6qcF==V;f>B6?g;108:9[>:Hc;L`m&Y>kPBr.R4:a2qH."=Uf:@(X&?d&#Q3fc1!*LpcQ4c)8UoU#^=2AcSLrHl1cqVo6pn@e*p@@8/>";Wo!dr28J_DW3AC^4Xhd`4Ir5X<>1<'[,:.Boi-12aSFk!n=G(MKG.6c=O7SWDVV5_hV>/q,iUT&SP%\RPdZu$%jrDWe$YC`.?n[):6FPqJ^hDRtu\M\Ea\Qrh[aX[QR'PP;rK%#n[*11=Jf*h.a"Wh2Ga*jtijd:4=W(OK-tD8N&u3=YSS%VTc.!c>tW*'M0S+:R5Oe;1(;dQ+s/?oC'SXd2g!DRksSsTQ4Ki(5bqcB)\Z9%K5HPcW;2.UJ9LsT?)2T2Y"_1qd2b9+_;.+s0BOI]X5'*4L:]q:3U[MM8U8@8coHn'Z[,U614!u`,4Y))#Iatf""n/>S3WG?DVo>7bdpL;Ug@)KQFD2U>JD'^I%mJr5#)%>!r6RCa`bRcnH,u:6"C1t"\%r)5ED=8JBnS1tH9LHBK8L&mIQIlPGW,*-R++q=!]uhAqCp-&o!/_0\FDKUe4=G&^?:;-*R/50VR1J"-e.F<=O#Cc3uTPsXiS-rm.;kMD;s-,LB=;ERj)[IN$&bp;l[?(b&!@Xdp`6XfE-bD1Flh[`?qe*GPT\.1-Raq>^7gj+O6Sf&,,U.&&2:ZgAWEOs^o7K9'n7D2<2;P>m`>M3_N<]`L_!tAM=qGJn,-uIFq`oqd"$Q41p..O*(=Qh5k.S='n`AC]]"B'^VeesRIAM>AYPSG/`Ck?(#+]M;+WkRT52.jIdfJLU:[O7m52e&UL[hV:0&7mAR[`C-WHl'@oqhfsMqYItnZ#rK0C(L1l[3E`/"7Bh\m*Ba(A#^uH.66X=)-Z5E06(aIeP;;2XkfDd8@bRhsG?1Ds5%Z8WuSi!D864'-+d\^_]UqOd2fn`9iJtA(HKTpemKRHXXSU-(4jIHD"nodoA(r9Db@_>V0"is@`PmTd?MIO8B2\@F!*_YHo_ItW>T&a*YA3l,MEp>MUDV@_<(tjlL3]C08.o`r2Y,LRp[.L?<@mqV.`$U3(Y6\(V/rriCV%#6Hf6R5`BK[RS[PR45H:Glhc)0f+Ggj%>%$=[hP2VU^s\[0Zeiu/bZ9n:?aR#p/tL&ktpHYEp5X9+"7D-?m#@b0hY\,rMQo\j#0Sr5[lR((<[YA5l.M6]@mNRXRRG7YGb8k%ABp$&[3,!Qs=U+WLDQB;_q6I"Ai(>&Id1pfpKP4=9c'/W[5_52Orlh0smW@14R'"OWsHAG*,*_4@h^XcCXY(O6M##$]=(2S_H1ZMlkMhcLnD!(c(IVYX\+HSXSMn->%OQB"](c3&5=_@cCfEQ.FsW'e5(`*/%!$+iJ3Z._Lsp+dMZ:c9d`B!VhVY!&[--+F/FCD>G4[j&-R"JZRSmhT*:c@N5^ck9s!d8)-cA\MLQ9MF_Ru\7Ta[K-.0d5)J*',bR8`$a@rdJ2i1Q7i1a$a1UJI3`."k"ja4Ci)\eF_,@>T?f9fd;#+([1,KNsJ#m>i6P)&C&&d/8c3Ftq?MXBk8\A)GprNK!0BMLqjubLVP,o,()CbUIm,2(C]p*;+[!IZp',utdY>`]'Y[A=c./g4[n?gjM4#1]I!tL&.E_:6,FB(9gf8fDcWZSQB'X-NqhI#*(._MJNBDU`g*IkP&fD<(fY5d1Uk/k6JDT(iq]L.5:<8U%A8I%M9"[U'UOjF0_"kW\HdFe.+rQpT,,K#@;EsTXJ<;5T3]ss[:NQ[OI2#[.fZ[dg[[+(O/j0,oh;S5qO4fZNHh5itEGnY%B]8:TJj(Kpq>B+b0?NY\?fT5Wk1rG/Ms3HUXPiD-#JZ5NcSRj5#Cg.%ar6ZFIh,qqpiie#P!8J@RLhN!@]>nAR-FXI_D;F$;uNfn!Ur[k8FarrWlt1TJ1fSL[Y23E-TF85p/No7BqB&s*Ltu$lVpG5\,N4@hB"[dZf^p!>#hl6Bots.7FC@'JMgNIl_R^@45$G_C3qH+!c>bDE>3+dR-)*(b$bo5gcE\(_==i1%6*78Zp9("QsFZTm2]Uo>>Gdr;GhnGRI]"&FVfitb@"%qbhdgO"qL#o5"bZb^5B!-OVTYcp1pm8uBCYTV2Ys9GRK&SX55skt?hRY]/+[8i$fh.Lg%F_1:=4>gJTN+?aofag@sXs1XF7ke)JBt]sMVVX[Cse]Q*B1ei1%>k]H":^J-ihfKM5qp'4oRJ'4*iBRUQN&o=<4_f$$/[Vs+Ih:5#^QOhc15B4TnI/f>k`dSmZq0l>9I=-34V[f0oSmQ^/r[WDANIGg:J"Pj"2%4s]rgEd6O5DTPHK_Ik^UNoas#*BD/b8(*`cJ`=i\*BA1:<`14KY5tM-KOrdCtq?'Ddc"pFs@SbI+F:c/%W`1kS\?AqpCi3dJr@1Os"H)/G"#%YbjhfIse2^TVucCO`M]e)AltIG;:\jRm>$ja$Ys=29G*O+XnB[)&=4=;8CQ,,0g#^I6q`Hsg4oi5P%_U]Df_U3&Z:$f#ef60N84S,/BZUZUS;r?[Z5+mt`q5DH?'[7E*cmHMhS`a9OprEiX0e]FTQn+]'J*CWbU6.&TEBt:^TQZ2[Vm$YA.pG)TD:TEpIGM?K%o;C*`H#duumG%*El$`Q'c@%fNdp*tbiHNjMTrM)p0_"[]8DX)#H?/qQ)r^>mSJf^\h[DA:[=/R1U,AM;:%?lR5hrp#DEMFV`K(N`HG^Qgq@AAGH@\B;_h6.>T$]oYAu<.h?_cA*8(H\p^5na*`RUZqX7p9h@lmK$juTdDp36NX;pY;9h>7,:@ma$fkPgS(EB[`e]1JTgS9pn#E?n=)NuH=ZnA>Yb_Ju%m0oYSuSIP2ACY+Z#0>>k=lFtrj+1<-c^iVN-p")8]A"[d-HbBtL,L3\.kEo$='<'CIt*JSO9_HS(OR6;K'Bl#H>;WQ]kd*86%[9?<$4mga2UK@?jnb[_(`;3%1G\VKt[CGRNh@e;Oh^\;:LLXK3^Oqh5rlt\Qnt07Xc+S;IOjH2iFtrZ$;=*+`5n1"ue*3lUp)f5UcAT;__5bc:Ia's..@OunA:ordh&[Y&A,-#[nE^"7mEG3pp=%Xl>(CVWl=7fG)(ojrn#re;mnZcQ^P%$%4jiBQS-`c0_7P$-N@hj,r.bQ`/fMQ!LUn>9 "cL>tj[h/mP4e+]K0IgrB(IrBl/X6'*HVNRI`cW?-jqRe%>>'#%>B)569esGH;%LJK7ng)'(K:-@2\O;qWqY8`LSFtVIbGP5nl@5/3CC\col**_u)5K"jn*5%="9[r\IZVFD/Kl1=U4=+blL^)`5iW\.gN36u8a*lOjMN/8]W&3rP18%C?sL*NG&@'M>AA0&4(^SgZu?cZ`c#P%X3g)nG.hO^/8DrI:UT"jnLHdAV]G^h[8olg6s;&]IV&4H5er?mB2s!3)pV5o[@r'+5S0"rFT\'OQG^Ba)rlg$NE,P<%+(\:%eY<;87I5l7r>5:TgjM@?)@K.n>ns$kXCVEo:\XnQrbl>d9HN%^@*+^@Q@,1DrY3iT!Z`1XtlM:=l5Bf#,Qks3)=odPr*Mt'RZf:[sk.8an[1seYkNC%C'@2u-^Kj$,`?KDqR#*:sZ;[+bpJ#2+rf!AUn+L*/FQ^,&YB*ni?["-35"G,;1&EgiH[Ha'*lh:Y>$4&(*;r(H]'%PqhsC$K8c8;ZO0@624O:72h`nI=3BtK4e!op@gYMMJfBJ:@Y'8GC%YL[W+M@$KcVM:aH*d!U>k-pIL(Ib6a>f\f$=5-E"7Q.\N41DW@blSB`EkU^*^1VidLj.J`2IKj#Rm4m7)m;tRg[PnJ2Du?l`hr-3UD!6%aT?A34/QBbNSB`U8gslFL_[WJb#uSjE05#,BBg;^*!:OC125iFI;m@ps`JW15+Z."poAP17"(/!sh7>To)Z5AUfrDc4nYoW^LRd!A,#\)g@fkFW8VN*:4t:4cK)/R)P&Z/Z>mg.Z29Va.=8pXQ^CYjMU;q]Rd?-j]f4KhG,<>P^88F-!,r^@khTgLp83BUD+:g7H$q6JP>N`6j);q%`@WL\;`j!'d-s:_*OsFg-0u5pKl72P2?K,SQk(NC]&[1ICH,No>1A=B'K/nIp5f^[;_2lqEC#;blf6ogtY8+=k"Ug&$472K,oRG+.VY=)K7F%=UDD5U\!aHrt\tdhkneV/0P;[\)2c85','2\FgBq$(7B52K$#%SRq%Kooea_7V$S`Qq5Y_op1^;6aoN.mDhC[S/T.[obi.CaX`X`q6-Y9@]Nn]O=8rDj`PLFi,D>dnWaO0X0ud-\Jq`O5TQ0C/*$d?ej#^V8DPcc5r(^TGH=qaS`]Bb>mTg7of&%-Rt,9ahj[k;[A,kVEdF2kQ/RK;LRN&SstP]M"HRaNrCb2pO9RuS%!=b+lE8Po[1,-ODc.7]?,;Kd\0%)k[?!K@/0IKLa^eL9/6o;q1\JHY'^eA$;c3LQ3;I7Sle=0"hL2!;Gi9]2PcofJp/V&,P3"2Re+i#pX&,CcS7>;hhuP]GhC)YYGna&5,S1df[p"GB%,MIM"rogYPPs`T=6R8c5j71Rd3bQlD/QNYK[=FGLC/;n`j+SQe3Ja(dVN_(@V?,k9LitsELSrO,P4%V9a)DL[^_;o@jf8Ohm22[ANC-jEff8S`q#D#'3df5B`%j@,.*-pT&5tsaFHT(P3M24=h*rIiCHBSJ+EKg.$\$ROd@)5,#p.3+#SA>)"+/cNJQd:j=,X(lPhqlsZfbPEUqF*%4@#f>\]k_fu^#``XYNhBHm,AV]4)EWefZPd>V%f)oLO.naI\aK<)J4/OYjdo?Q^&ULOL,6FW6aHTdSPB?Bc+UHd"anC[D\BAi\K(p&/a/R$j(B@4'9>lGIoAGLI.9uY021&#[f]bB<;%O]5]&Ui$ur)4sF+2$cQ;6;_`B'`EA%SCuHroH^c!nPAW\X-@XE`[)3oB?u5obX5P[a'-41/#n,):Ccs+@!<'eXSk!HY:HQK4e[lj7'Zp;pHse21IelucGiB+1YVfuEG_XX5o:7V_Btj!q8Q]ja"R)r58q\a@e`s,?if(*1]@inaL5Zqb1u&KfkA1LB7Ykh\2`p\u_eGI*1)U(0l;FWV5JBgIPj7in61_K/27r^$*`U74Y(E/m)GVJkQHdHaB6Wjo[D,d6lfN6DVdnmO!6i9r7>cbABtCS!MmrdAu9Na?ZP9kZD2?;6O73E'f2iML4t46nE??#Nmm3WucmcPdNH@^i-N>.N;.,n6p5d;:X2DZ5fUNEP!u[q5lD*^bo;::Pshh6-PF\X\D9CQlSm,Y_?5OduY2,au!@;pAXPr;45kSpBCeI,7Z.Tm,;D<_uMZilq(J_i2fLB2qA[X\'YV)X"7D?'=FbNSAdGI(VqDf,PZR>Vi7)Ra!g7<4sqiL2,;M;kJt;K/^WShQhc8X9s&;*HZGIg[S:XCFN*@='W1U=aO]/.u_c6D-#;9qE?uBLFmZ*61Sek>1'91G@tQ7f[0^=#%rg^Jq2/UPpQcf,U6!9l\Hc=.?:-]Gt1VE?`pl=B3eEZRkt\/NL"0[%Q\%=g-^1N3or!?ouJG+1un#&Cnth0[5%Z%*^:fq:HlHN@jXhR8,ac&'%b?W?68509j;WUo[[sOV&O$BictCfYiH-Xn3-L39r:NU[o4IgUo+HT$DPn9.k.#+RJr+%KZ-P0\89&H$e2HI-uA-6K3AK:j"n=AnY9fi6]e>7AA7:-9(];3qmbL8\6]Y;lH1dYR:1hh3&a?sUWN7K9k+U2YP!ikruq((SajP92-0Wj;Ej`$CsA)Z#95U4Um&rgKDT5mMnh1oeiVT77tWNGO.D+/"AmDHRhp6^GHF'=:nqA>l-%fb/gM4uJU5S9Ys-.L4VM.eUO2Jo?HT(SpUoOt:MS-5$f(pnVBJu10XZJ+_$Di3Kr+N?FJAS9S\gAQe.Fj9AaHU6d:Lqdi=^s7jZWKC`,J5mh!gSMbmA;aE4H9lt8K^2K7j@;G#IgX!\eS:*cES#&`f&qIUT7bD`plK>FOUQ9'"dG`4ZLU5Pm4*#RHLFS=@I8a;b\c*-mOCZ#YcHE0rC/?OYg<>4%'tU&b]DP:iap[XYSf#I$pGIJqiC3.:b%lc[ZZ8US`]J^ukkV`:*B["TEa%"WSHNo(Rl?`te-#7i_2+*eHcP+L>BTb0XOsNYA_eIKE@k6EQLLrhTX?#t,B(S$JZXo^F/5>(,jJ;4+8Y;!FQOk_F^7;A2i^VR](V3)IU^=;2+6Hm5!k0:6`Dd)U$l"^:QDZ^a`po"JA(e!t2Tl`K>&fj!ELWCSL<]mGdkn8&W=J:9O:NL0Kh41u4QdbbL7baos&H;._]ija=t_VE:/@7[:/QhKo:+!HY"^jf.HXE:W+S5H.U+)uMQ7*[Y[\ET,)S!7g=?'M`ELJ"<2_:ldaGA(-^en`m`hq*+0JcuN*?cA>*Wdd5sVi5efT7q(mLqIRB#fBH.DWbj+cZY:?J,924.p8=.;2%G3>d8/?_H&71?EibfbsCRonC>2MLlI&XL_Sk>Z,=J^%2nTj(ASj\'B2+W(a;;'O=A2S:W7-PuUblMa4ccq<.4e]30$m2W`-:aON(NEQ;;'4e*eOnh'TYhD*uSgVYD;2GWY82>]Nghc;7)`2/`SB88!QYX$o%_.+7h`h@pqlH]r>nQM#`YKt%?D#991PF#_TCg;cFFSXoQ1tr$-%]KZi?H#=(iG'jT>4Qtqf7>uiW63&[Xc1?%^VV\\S[bWJ4R]F#"I,qD6X].%dVI$?$btFo[qK[JWPr1S,ClhM%jVRBejXm9ej$(1P-[9Kk%\n]A_QH5BD9DGi>4(O8p/qc,<3#sWoB:[kW*L.(/4MuTb%*W2Z\l*>\LLA%e#oM*MmuDQ*[cJ8*)Z39H16@SjPs-1%2Ia))\'[/:?o8q=Nh#)4W9#f$7XQ]B''8c6,Kl:^@3ahq)B#hGe2:BS]m<@'h(@g,OA/rW7jWH1p2H8o]F)i\St%NRU6=CJl+ScB7#IIWkBU+]%R3B'G&rWQSM(YBQ]dCIor;7FWe;RB0;T^QB4PUXKQG8rW7cqVc)oC6C-@'42GePQ[?c"](=]0]b:)O?;2!YgE?4jqf&RNK1YrUS@PbG\c:)F!>1h#Pm9_fXX!I5Y3,Y%)hd!MA[,IJn>:pHBQM792Dmi"$::jM:\!3$b'6V4_QQqcgR^LXu=glKdn$J?@:TA!4cU@Dh.8P@@>.X2]=-:Zj,*d#'rp*-&Mt)X'>tF9Q0f?&Wkp0-g]*P@>:>1kj'D@h3-R?U2D]HkUq\"I=@U$q"[:iK2&t#&s@aGdT#tArYJPip6Tap'M)nhJu5LV1uL.uP'\[HVO=Cm]2$Uc/9hq>"Zoh=X-oG1&1lCA$'nMTV#kfng]jd@1>T!H+8=+eqGG3HLB?;&bF-8X[!)L'Bp!>_([n_A;3YG9N\3b\&*8`h4darq()AnH&Q=dAu+.0&_`djh6R4!j^SC2j3nK*nPu@ZrbK6SG)E-5HVh(AE]i73rKDP/Y$=+XNn,229T9DQUi>1;ogJ\jfgOM1[cSb%S=`33+VPEI8.dhi\ND^]X3to?aSV`H)Bj+Nc<'uom1jLf[&Be2X4;r;GR\6[Jp%cn+dQBMF\EKo!/VgV[9V]L2]5`#Q3NtkP\<5'81eg;ooDd\=^nd;n<;5.`RgAhEABXjho\^%Q$&E30:jUR72;&,bak6m6g:`8W\%oQl64QDAt*mG\h9Par0P=Q(TaT@\9^rq)J+8m`_L:5l#hNW[hD2P>KmW?qUp-g:8#m.o*9#C9B:1N>q7J6/HE-&J'#iFHZ)i^fq%B[IJtF09^hTW.N!dBYK$V4J^9amWp_B.>^78qNY>himBfmI]i0T.LG@AERW=Js"?BA@L&85rNGj?sYt+aINo/(:&>e76RrO\9+5)[[3913n=$T/KM$Zr&^fUPAYomHYojD3S;Ku`7NBa%D1D4\_k!rGZK_A56T@pO%p);.8CpG06+Vdp9VCQ;jq$@eGA`olPATmBtepmN?Lh`P>4m\hr?dBPAU)=fZ*3JpZ.B7W>GmcL7Sht$,_IMqeInMgQT(/D)At87@%jr9pJD"r,<['h660HL0!%[WCA6)d]qt&.MdVl@jG,_81hgC@%/s5i%!_C(O"A1^=-TQ71q7[2.d7]+Ra?)mXCF:tB>B7"hH6[2RJ6=8e.^^S*m6k@e";@qY6&"%n@>(3pdg@?(jT[lpSGK!/J7Q/Eu2ls[kUZC_]0GeefigbY!o27HZG_^-EEJh;/t^\_A:8Zf>]>=CM[c[;0//%22?MFW;a>I_k.$&8#ql'28Nk4g)\A[ka#q&s,.=bhB;.N9Q?,)WJ)ZEqtKQSagVS_Il],%\I@[ah8ZWZ=nL2A>UA3t=5AhL*`Va[hOh^7..,OUPFK&7be+2#N?cVr*WOI3V_)hErLXrcV*1-/N`.UnP&C8`WD:i=7nu9Z@bc>.?Q@a9>g0UD0?B[(XDl`[LZca8[dei7e].Rc*_!9s=ba4)8j'KASpf)N=[b*Ao=_3S^0%hlL(7O#`NI^?k^OXW7A#asSL6iRaBtX-*^0ONRr6RB_k"Pu_Zk;2Tc[_K6PH7#RHs;#<7e2COa>qIiP0tKfAl#ll,dRpX\*FLNo=k#Np]HV92j\JHrnre=DCj1,0Q9?S^(-7jV!Q!]VdnZUh@M,F6VH]ME,s[-t&<7u&56>mb]L.i!+>b4jNl7(.`I>SYNnfY&(`^FiX%4:=BP+!`Hsa&KnlLLsAn>h_3'`.@a7<@(V/t["=3s7ick%h\R*.jCn4"`Z/F;9Z^a4;VgLgpj+r:G0#tsbm*qXUK.s#*[]KF-Ts8Cd+>5aYAY]_\MP)pD"@:%d7]]D,i8asQ?AZf6TRBOM6tS`HbrXi7Dk=)-2sFsTt*A,)"?//c1/gQX'J;6f/)+X\"]M-5+b%SRMq0,+3H"s4Fp/g@eR[bZg%"q?UUD/D!"(Nq,J:9$_9$$]8!b7,D6<]k3Y"E:rSHTh-u$nBqJi=k-%R):'Nh@S9PqAd)e;tT5,aeH;^JGQsndIKG!Ijn;^Hh2`b8V;8hn/ZlFNDo[Oqj#nZ$9-+qWpiO/mh;a'Ai7LD?f"1+JA2:TYM,mP0,@&>1R%6gsq.u^))'^O[,j[Q@GDn`m>?h-Ak7<#d3phb=;\Y9a_PHB\I=$u?9b/:-r]E>jG@dl;Rm)-[Q"T:noX+i9Z%93gR/UO07u"^k=4r^lcMZ'fPGcXlSNdTOA+5KL"ZI+*1m;oqS7CcnQCm,nC^K43KT3D;>+9fN1HO?PR4B2I%.(g4d>F^__q)q]#_o>$C`/&B7*'r-Zlki6F&H&!$`@GA5aGFij.Sk7UddcdrNroGD2es@X[MAe=)^(7G9%>MYE)2*L[L7gbV:Tc"VX91rdL^NTSJ!B:-h)DASLQB:5D)C6Q]*Kn0[rRMRu)>=(_/=PC5+69qqnT%4VV4L+fjD++L<*PiX9-#o+2*m8Y/o[HMjb&(jF"'D^^H,sdD3(cs_sI#PRPnXV>dj\S/^F-G,!RXIgG>&+b<5.$OTn7*i`V]!6k\!1RRY7.-$GD?:(n[m,iFYV908ZVd0aH?J=]Bbj*:bPA[A=#"n"%"bIHUX\75b$e!P]ZYu]LaX(SM(K@W1(;k[&pJJ*>O2$5aR,'FI_Z.ub]+>%/)!KOOeN1&F+L_i_%s.M]p\FR=-;K7[S#<[MO:Hg70HoM"^U1Kqh4BD2>k1Z#PQn:\Z"+@*4tB>pUgP8K`#B1G1a[bYONEodK>r,MQ]dI/>*INfMfOs3?efX/C#nhs`Y0eoHT@No\3;Vd^e4Bqrr0taOS!$8E[=YM4HRUHAp8staQe='D0CZcj8RBTelMHLdMU@K9fb&"O6B_\-W%Y$0QBODod2!Y@]X)sjQ;n1AhRmuZ,EIY>=EUPm[4U3a-Nto#oaBBT4YAZ8:DNLTdr9/:1f`J9X:bU9bAAA.*f0RnZ?P10opArYFIJI0:I+`]Ok>ZrJiYl5Fu?9\\!RX.IIIO`ok8lNpi<<9G'JD.W2<+K.tZ:;RV[E,[=EHS_o>#RSu4p1Ld/=gkU4eCtP]/*KgQCiF@f>gWVb6!O>ZG46@;Q;_,cdZfApEKqhn3UdkYjJ@'`d?T'JBF%ZK%8U8l=fN*)aI2:.lbIY<%5i>ef\I3Y#0RH<\m8]hM)N]l1.[te-W$r7C+.Qoj+iC@0%o]t`hb#5@2$m]"jS%E]IAlSFD$+9n]_`rdC9KQc>5it?Z4&sMu_+o]E7a6$2R!AOU`]%Yr=5@'UDsR,VO>aL'NQZ)_A.g1U,H4M),]g:iHdYo5_.Cp?+gmm5c`T(R^&ae37EB>A`S"Y?Y`XV6obcUo-uH2.,1M"rnlhX5=q`b=&"@c_#OcWJ#0+69>&bC',b.fR-;Rn]@WY@+p&X4:OWg*.:Hona)A)OK9:c&B;Vp"ak:D'SE=(#RfQljgK?._Mn,8Yc7\XAIbtg+Z$cL8DL4..:A80%\+npAs?DY<+;Ud@ECS82Ua6HbH3+lCtjpi^M6&0fKZaRasW&n`WU/fokP*_6UnGJI1;8h-*lBa[le'<.jhLiB["*hQ@GVZ40OH%3)Q!bjd!Y3s?ob/sc(>pW-meV1r(_JsTg1j2+gO!]9GmR"nD*;sLbq"0P/M4EgDin$VT84ZNA8q5h4iS:Fc,&k=SaM#I7LY&>SV2N6]Tc4N@(u^HPd$5J2r"]r7jZ'OU/k\bHpd3al"Y$5AL^YA;Vp7pRs"3:K;uCdM<5md"hHrW70ha$qS-W5j[;lZ#CU&,fe(d-d[UX3'(qSU28$:[EM4Fi&FX2\o`9I#_F<%_3YY]&,N!c6IoP?q$N"i^@$s9a7$nRi^)/EX),u[PAU,q1-Bti[_1+"6e*@H@c(%BDjFK1D]Q=GOCf[)9DQ((erJuueL-&gm8e3ghpm0M\8S4-e,kmoW.ca11\,,]bn%I5@LMR3>ZKIQ5FWSTT&%^!V=W'm!\]phZrVMJMNdf:n+j2a0CgFnHJAG%nmH=/Xs+B)'Dt;J(8[^SE\e-??]g^K>[Jg6k4@jG27Oc\e]s86Ir4b#The9f]_IJPUcao7ANP-o@9A.q'asf(Dh\*'Leh/MA36!MnhkD*#j@u?'M[q'ccm=KMaqg-/T,(,ZJ0AK5`BEOj67sAk_n-`OoU6j2$O7h0H5IV%*Uc8`["t,\3?S=-#7gl2jEq&\1-a2CXY:[CYXo^KDRf0jq#5OOqTbd:UW.I2",#,&$\OBl0FC>JUSkO<\V@Gk#l5*O1WbiSBZ];2or@)Q7%R]b5>$in<65>FV,_%Z.$#jf),i$f.qkPX5q:lp$C_5E[.8Z#mGt*)Jo_ht5Uo^mm1"jDpU=XEst6q%;9;(umH"rV\]O+-XlAjFGQU$?MD%jqJZ;<5g@['J@5Y%O6QM:WGq5b9)H1VNU4kom#8a,,J8:pTiVrfme%mcNmh7d*E!2Rb;(&ed<-MDBJ8qloM@G[7P"24N-$=WTC@BULt%M%efC)`,:%%m5g]"om/O`%p0CVd!>ah7"l'Z0;Kab&J/&9Q]d:N_2ha##2.nh4Y_mtRLNrS:RQfQ%9sa#p.NPBB];/N(LMs@hY(\DTZCp!Z:20a?7%=(Z]]>o(a0,1m=@VpLgY9(PYPF,(i`>&qOp(k1VaFTf]q'tibXf"JR^iYV+UtXlj$b)[WI7e@8L`"B`XWp$&X#t$l!)3q57\r&n)#h9mgt!oZ;Yc/sOdaL[g9>^'m/3(WGPF'U2aa)t_.3b]@[R:!\G.ABc92Su63EE]H!2ap9dY'Z//X:]X!aJ\)lQ(bIi^)n>.=[%c@iJ]b@X%3Q0m]WlY"Q"O]>19eYISB@=3<'ub__.Zh*'GQ/?gL%VmIh"OM'>W35e(LrE\DVBU_MI1gBLqfKM"!47,ouj0T*c@n#B$RAThfKW[\V5IBs:mrmPsPNTqE[k+sl-UCZk1(*pV?]kMt]HTaTA=46U,jd*S/5d>ftn*KR6nM'u^ilnl//@PUSN8DjhE@]K,6X;`\J7'ol(cj;.qioG$DfI!MKV?u`M1E#=Wl3qeO(.BB8p";9hK[E^.=@WdJE1&>35([6>Dl%t%j36YC0BgDFSG\10l?aT54nON.D`$9qfKe\Sft%M09\A>"f'88jb,ZEL"IOfG%5a;C*^Fgh8mdJaFEE*DM0q-4$O/FBT]6c1:G]U$Z0-G[nkQ=KZGAt3_RPLF)qPj%AF]=fX-^f+/&j!E?ZS<_HYB"WX#Q*.rTiB-[^_E".@Lf->"R_fFf%OTu!N\q4;Sh0Oai%MP+dmkVg^Di0hnlL,EQSJ_D:9A!p4.E_jO]d"YpenM%ljblDfpT-S$"O!-1r8rRKVA.dO>pcA)eiU9`ndSTOV!$BaGJT8i6tONd=GY?:.p+8XMh@HSMLJnXN"%nA#d,o\iVafFufkZbu2_f4U!-[.EL)ERAN2E_#-r]`#>3ehR.>m_@j4\Vfb%)h`6NY"'PTs3aeJ69.2cYs%/Z*58ce04NS'S8`!Ij]4:F]@>!:r&/F_`[ERV0a:pR^8_]3M\YU"/3nW8\"Tg3Xulb#5K5P%%1d`/)oY%A89pI4>KG70/#BS*cS)E4JY$F7*o@s!9Odp$B0R.cc=ng,ic[&%h7]RGB&dtG,h>f+I2#(AA4g)TMhP%)EBPXSECELYMSWpr$ermI1/'aNQZUK8DCsNPfL%7@85IM&;PT@6mF\O[Tm^)BUOHY/9@[Z`+]U.c3fa$*Q1Q8t(TU6Uuq005Q,kW9dV^f7B;f.pbU1f('B%\l8e[Om7ir6%u?Dc=bX0[7H`5lu=A?0371lVu\C=F(i=aYU:[t3eWPV]a,=T[X:Q6UZqh#\54Pc01q+YQ@5q7ggNZi+Oeqq(iIk.:N`Y"9KuQS0bY=&Z6V[O(h4rnLKc3+(?Pe+U*tJ*7JU&#p=N5U-&Q?FX#6s"4F]VqeRBP)N]9FLG$"%cQn/1b'mF>;@[da!FYFm0S2-0Fb"+:i#Up^,IUk/ZWrcqt\=rj[,%GJX-f&ep[_qfQcLr'&o;!t6fNg`g+=,TkKI$M1*I6H1?=`[gG0O]A2Lt"Ts!50JVtRq>CJKLRV\O.2_k:1IFFSU[#,ita?aP-H]oATj/#qJ,cW1_%RI>:ckno>4ZH$R?MZO%*(8AC4KD1.VM&bI!X_3M/22bMKWO"SsC/49g1`sQj76>g;>h=Sr;aWCCZW&FQ4fSFihr+D`o`%Fe/1g?2ch/-CkY1W80UkqPMq/K\OlrGiG$gL)a>(q]2B\!'RR'+-bqkm'uRtlVX"oCP*%_0<9h00]EVDK1Be]XFpH+i%9k8P=7F7=YbK]Iot+Bgi)]d'3j,GF^/]SEV*=rN05^/`:4i[APT/MaRMFujJ/de?HXk+OTg#/fIS&db8/!]<3C<'MJdKdYkr`6mu=PcHiPm%jURresdBgI`LlK@-ao27khrPNfH-ike(J^VX^XQmqQIO4gCP]e*sLKiim!(j65I>X1-Tlb#$ts0/dG8LJjHP=b7]+g_drQ1Y7sCN+(Y!].8fS$eZ$18\d_*gGR$OB0\(QjQ3uHN*M+g+"/<-8?F'uAFi!bM'-OInWnJ_k`4k;2uuc[N=0"7]2a_6lR\Vac]%8ZB[LkJ;FAp1*CR!Dqf?SL.&i1aJ4>XKK!tZ^0%/5G'bco;-97j7T'PlAlBdgp):i'sVi.b)?kq2*2:\MQ8\+3j/0Y;Wal\bU2cLK!T\LTM^nYkc8GQU2RO)qG;jl"KhtiqLNm9Y^2ZXPA"DZSd5n_:9Hb4^8uNkYkWG&>S*)Wfhp^j%LNS3U6/Yk<;nV965T:/lhi4-AB.ZpR&WXM&8<*\FnT#fG&?\q@@Qfh9?LN;lP9WU<@=UT1"s8sbO3\ac-=LVa_((?'MW82MGe?$IAi`monPD*!WTAV%CHL2@Yp5\W8@r:u_.E:ol^7%Cfa-/_d'=!NupM9PVr=2,KffITR6'MSsY[j#!G's/]arq0pWlB0[Nq<*.Lf!E&6]X]Jm[7Nh5];jWKS\@9n@(>NT;BsLF9<'LsjQ].sb#M(@G>t-enWdge@8*<2_0gOb].iSaH+_eH\"eq)odXqF^?&hZHiu#r$J,5$CQ0`Xqg:=^aH67-R5jZ+bS"&%h*P!:1&3D36&9tIBE&!L>YL>EVigm58>r=.SFYVA^Ki0SSA;F3OA%]`A)V?Ma\8gspC:=^PYJf4Xs/NN73*[^bY@lD_8b%oU5M-@b=S$!$kUt?O(snMgnlVpI(nS7l\!!=@Z2.Z;P?'db_[9Q=EcR5g8^L0qjaeS$_=qc'PC0_1Br)F='/GoWhV[0;L73*)NqV3J$M_U\\+cgLP[S@bB_CT9d^@TlO/I[7_9?Dt[`k@r@OXNBk7iF9A-<0_#)]W=dS)3KMP3i)OVa_Da8rk%-Lo[aW)@$/EFT\X1C_;rDi91#1\fIL=@TGRW]c6Le9seO;"Hq#4_s`5qiu\AXMCmdN\%LeH^d)S5b='sg0Q>.?b-o_F*&2E'#ZDaAA!Ut+Vd6<:"g>/;"eYVuerr$eP5T!6\%ci)9&H3LQHcG.UXl2m/9M=q]DZ#7GZ/MW(M77s'mP;`9L27tDjrOV*6T0i@4,><\_(-6&bi0YPSfA)4(pmTOCknksYP#I@NQkH7X.5Uckcqn#V&5dFr#*^*-B!oN"1b)hMI\%O4!(kpK:2&=GPHK(ba8T)Rs-=/il)#CUaULr^O*:FnZ,bRPbC]M3j7-R?:AhfSr6-bR3Hiu%+A&e#$lFsmkXEq6l"XOf*!+KAHEZ!603Z"ldF'0hkd#V\oQ4t\PgfK*fNI%XE_d-*@dq6OQafdAWZok8qs)fRsFDOP&kC%3uKD66232h00Jsq'68QS#*.XHKmr)Hl\OPDPk]*QnHYpM.KJ!d_u`=MeZVbN?D3?2#*j*Q/+XOKqj/oKVtX.0qQbIROMfp1^+J/f.qIbt=aNB3]2\.G"oX0iAe=pX+!GW#P,NuRf/IC)S39J6*R,l&&m?76>pB1Yf<2NB6o"6oo50UZFWrs.LDibl2HSTY9N&9DVsj(('4)F4D<[C^N^]Vm)%$a!gcA`>WVgM(o6(.%9Vof6\8bh)5a[0UO/3&FT]87L(cnafD1::j1/14%15bE41*@VZ_dW=pYqh^ILD_5oJQ&Ln1C!MV%aK1qIHd^>fc(XBm-0H,r*IW3XuU+!LS#B:PWka@p;$9XN;Z;$Bd(JtuNb*AQ]&(Yu'oXR`P5l=s)TP$F;EE_2m!YbRaeaaI%\=4Po6pN%aa1;2\11P/=30,MBqaj-rED]AGs4QV.k5nNG*aA&as2fbrHuimb6B/FB@<)U!6mImE*0RQ";int76Yr_eg-,g;WI->7A+f"EaMN06W^P+U,G4elFEF"iXtGMg`R/A+DY7,YVB<_*Q6Y-NZNqTn2;/MJXO)lspN[&=ESU,>uW3Uq]J*rG^E.&rI=QK.S!MN>jiK*ITmBkYN.Jb8(Fo@+h+ia+Em!hD_=lNN^c.\IC`7[n7loak)s:bt7LcAXJ+U9pF56&O8N5H*UkOf3T34?f8#'CfE3-o7p`%C"bkmUK?GrX/9nrQcnrZs6rCigiDSa4mF%6?Se0UP#B6,f!AmZ.bepc@8I\Jip'dB(E]c3)fckF832+(,J[E>t<1plDYrSI$!MGO*ct8(PHR1OHl@aJ:Y;km!o9:&]4qO7G-kj%s^5Znii&q4AjO/fc?BUj/(ML64W9#3b88Zh?RRrVN?/D778O.+uD#e<['#-dVBiOa*rM35Ak9(h)AQ*PY*gqgM+S:o[YE"`HBa9_e)e-0)aHoe[Ca5lI468DcHQX,hi?`i#UBc&*Fc;U/NFU*9#tLe8aKI%\?0,ANhK_Eln8F`*.*3o0i>"hXBGX(F3!%"S#.s@IAWq3]-tM-JK]I>X3tDD[4DCRPDC#ulLB&#dN"Z5d!%;#B$Z^fom)6L`GBBHY#]fW*k<4Yk_@M=dHtD5^jsDJhqq3(DBCg4#D?A]p=AS[?YNL,c[\IkDi-s,IrRA,;V/t"Z"FWRI0l8$aZd%=3A6h70hP4bScA`kbosXTWQt*eE0]iP:Kul1X:.'_%IkjeUC7%R7;7YJQHGZ@HB9unc_2ucc-"0\KoDO-Ll0a#8bM=\LrH\&F9eDF4lS\A6Hi.*8o&Z?R"Yr@0ZD>oo]*R.8`9j+4OmtK>pMm6AZOO[*BVjUY14nU)6o3rNcAlMYZ)o_6CB44l"A'+(aRbVHE:#knmIbrbbX_9d&:\YL3_f1tgTf@*D6nWI'ZnlJL_G#(\5"4bRlmRDib:M69Y\nYVT!CTaoVeYT_TIJg7u-Bg(FsOj!mG.g8OB'WG6RD\6+?+rEikgmQQ*pcK,\jOj*(3_B_^oP-.e2;Up9Ed9AkTP=QbJn*NEjuU_9\b'59PG'l9?-`*)#Pe@:3I#6_HsI8c6ET;$[%I9o+$>\op6b#(pfGrL*bS_$E7`Sr3mG5D0R*D.?%e^r(Y7kLcE)A99V`HOrsc)Ooo7^`_W4P+M8X:\H9OMI)Zgkh^<.7"nX8H:B#434P4'qmL$Z/a.E"`il$#BKI4Vk*::_O=_Nn$FR#&?$V=H)RfGTp=NZ^KO'f'5W%0f(Q"h"1qtot\n`2M\D;+F[9HZ><'G6_p)48OMKO*"8=!Nb(hLVDKD[6Kk]3m19(MM3EIFJKcWTC]Xp51kbug?]4,&$>B%b!43el"n`rZ)9nIkBuY?2:>eaF2*251PpF(9AGt/7hTn/c0bTT,mo*O>b(Hk2n4c?Ij[^^`_k"9H"t2c+cF6pJP8.B:%%.[d';?:\K,b4JC-3OJnnE8JYq[Y"WTO=+WcBOQnk01Oq1WHKtPp=?23bk\R.BCD9ZS@$I:3bcBcmc"NDG-q4r!`>o@*@GKDBLhO5g04(GUjj;^BQW#n)/]8ot$2PVJ)YC74e*8KNQd,3-bSgn#R(Ia`)*l)q?e[FL&J3>$"=-C",_SN0!%Sb-'I1OG$VR"l)q-Ogp5rlA?smB3hS-BliQck?DqsN-%(VC7-ocTrsaj2U2!Y%`j)"epJEKA!MU^-p/1?I9XE/jnJ0[Ic^[tA+HVsXH?%.20^n]];$nsF-l"M/1s:[#se0KGDGaj\d.n4&pd&tG:3\Sj&O(N[=f-OLN$W9_@Qbagc.#(RH,FuQbh`([e?C21C^^%HiA"@>/qlQmV$27_$@f0"df>WJOf#k:1h:diqEV09aq;4VJ`X3NH-oV%QaK'Ls`3E/crC:3_q$r&IC`*1,$b!^gQAT8%7@POR8/%)Gp;?ii;?%@9f1Ts_Ch:uX9f+QaMo%>=nXq4u/!e;YR,dK.[%4pMD0Ie!1)0Ji5R,"Q4mbh(]m#P[lrW:KlT(\'Si8&t?R.Op%SB-38ThtjDI6e=K6$-UB!%iH&^fQDZ7gh^kcLT@[C606g&FVeA&h6MTl*M&(g\Xs:-lMQ$VTflqY]^R%"E0)t+nPdulCie"fliQHs)D=pBFJDC-#G`.)]dWBX^1[b=mDGm5$G#B#kLEkta2H5]#]pJ-RFXMBiXA,g2YU8-e\mbicoAMD%VUaH&YH>r\b:I+VSk__hXd\$Cbc8<1#^NV!nRE:8bANqU$!X;pq\'0&sJ0,8k\?F38LNiWV%TnJmo\Dp+aXX8ht+Y]_P,)')<:$*=4Wjl5I8uF+2FF>!1Is%rtQIr"URPe<[3Y!U^$bQX`/Q9iH^R'GHlSC-IjGUm=\E0H&QE@k*tpVLVM(bW%@oJ[T!Se1Oamc&dp0*-qDl[/F^P@h`9s"amoUC$^mYZSPIn!fUnZ'$MGKd=JB2eetE3pL/K+$;Ym*4cOi8Me;,;7m8Z#pDR-ZHZf_1]Z!(/,bVpl>$Zno+?@QWr8+UPYH4.M(-?o/*4E'/1>PtJpnE"iQG;/Yp)mN2fnF#7U>_`E+nkU;MsdVK*sZ#F=-<:1`Qa/dWr5?U`K=sO$A3ZXNuVhVj70Vs*?r+@Sre%"f*O[q$<^N^h?!/ZOMB:gk`n;&0,stLOGu0[rN(,DnS0CoXiT]f`s2r;HX%0Y^a.?K]4!W]Pdh3ODs1r6q&C)`NeIn)EL8mqF8QHoaDUfTriH^O6kgMpBDb*"-YYl3<)7(WI^qnoaX=MX*=+?3!)(LcfbCnbjk*_HG=E*bkoWg."*XFq@G2CJk>CH9\mAT:BR$Xn+P)]ZLm76mRR5WoQYMD/$f+e>sBe6rZ5:d:I>ASDL@/%_4(-'[Uf!?hb6?2C@k@<9cgFahdO.rr@;6GRc=gD"7h\47ZL]*9Ed-pOY[djDp!Ka,-g+#^U3\_qLbLCr2W=3k"F-F=W6[=%[\X`^>"bl]A*`[,'Kr($e7\\Dg1f3hmOJl=i=E^0)4;kJojT&=NrunkG`Xb-`b'P=!WG&N#)%j5dG^?B@:FZC17tm,U"k=tC\U9-FN"0t0K.GdGbb.Sq@CNm.JV4!I&h^*Li-?kqju,Z\!U_1rGrOtnSo.rkDdT?SDHP2fV+[B<,b4kf,;NH0l"";5+_j`.'LtFj/.F1\Cn_;%?Z90m$SR4nP&NHN^L&.G[7^-0n!BVDpOHtbe1"m/2KtP$m@mLaF;Jrrh%VaG1)?TnHp@.E5OD)!L6mVK)N#:k(Z2*0Qf,C[)i<9os>2EOE*VM%o>-V3#j.eK*@r7YRP%CJ?`7M6bT:JFCF:U8E*?"h\e>8?p/0+/KBR'aNZ`O&+A;.gU?*N4<:$l[(m^!9gl[0B4AX)jbjDM>"M'_IlN5Kl@5f'-pUU3#[r1?:"!;BNg5g/-B8sj(dL;43%LsEct>!H@>c73:FS3WaS4nenolX>][RJ.C.*L7Y-iW?Cn`K:#k2`gJ#4[0pIN;a^Nm.I!*g[H7sV".i?]E-7]5s1@"UWs.o!Wk^@"`=7s(,gol@dlL86IKYncI67D'q&_`T1<((pou#MkJsQ\&AdaoW/K]C]nA8,AOsBlL.AV%adG6Bo73,_fUqK_N8M[p#&bh,2-B\e3o_Y[!S685`nPM[eo<^/'I9L3VF@.d>GlDOBMOUfe@=ZA7fS,4'BFZ@:]Fa##9Ej*U6^HP&&E?$cMB'H;%S?E0(+Q=3@koS-5J6pp=>'ogVj2\%=!dVYaCI4c00jS#5aH446gp6]]Leb#[C=oPlhlD[,<:H906g_i&@;Pf/I$l9OA'>J2Ud9uRe*$Df)@W?I#@/d8dn-:bUa^G_pudNG9O#]l-rO]dh&NJU$D+!D/uX[r;Q"$oIf@;&Ki+A*sqWWY4_;de>^4>@B1QW!2Ob'5'7M0:#s`s*\.^Z!!X->O+*Mu:W_(gqQ^-H7`E!J+HY+MVt2A01FhqrZ)[Jbr%[,n2r0!eOmX^i-]f]XS\_KIZJ33bcL"Z5Nf:?_Pp6.+$QTCOb57qFfO+`cR!e>ur`'8m\[^VAXMF1X<0.89eR9db-#<">H/?LjmuE&;djt81k.UQ.o+p9`Fbk!D#JT9fA)-NHF&,P7:5&.;`'b'l\1r]1B49^V"XU4iU"XOD)XlNfkZHPADC:j=*KH?V+QjEfFZ$.7a&\:hBK_K(q]LBm;F_CXVLB+1+B#MW."l519@U+0j,#jh_Rd'##IuJ([N7q5i,7n2GI$?aMn0C-ohTA%:c7QJN9-mjD#V^T>V%(O&jh\G((%6EOF67Oj/pqGV3Y_%P>IBN&<65^TnRHWF&&W%[(2b4,ERLCI[eF$hT8TphsRu+a?*8^pmeM`]ZFS5h79DORZr._aY?sq^E@uR-JVqOI>m!POEC0G_nrZiQ@7lg[LO^N'[$S_TVADfnSe0]rh"+`e(F\aI!#GAB6m7XmpXBT%4fsQO,p68O=E\n:I'd%,ttk&0L'omBr5h+pc5Um%+YPbD@NAWPKAd-S1i2KBYA"]LF@sP)?uBs(&>NLCch(RQ@$:RiQXXb=[dntI?[M1O4fZ[31I2lBVD$2dI_T=%qa`AjS"q1dAjWd`g^8SRQL:,Da$/>n<**8?lbdupn&,\I.E/(6q>jbXfl?=NL:X1Cu@dR9Wjg'62LIVbQE-a6SSY27046T8I\%tf^@,.RPK+\]ZdrW%^sgDB6MJ=\t/d&`B7bpesP0l2"_E*HjTtZ,qkX9:*hatn5Pbp+9D*?]0A6k+]]*V_5?K0[DrjnNtH4iB&72OX^V]nhE#-I2pH&pn]$qr1$o>I?ajg7;cq,@C>@lohO1Oe6c,OKc_oFFkE:iHUYa%L6cB0hMkJ-kP2JqY4'Z[q'qe\]JkPnZM]Gl8MO%/C8Z's,IHAk%me,D;cn>;Og7J%QTZbGE,W1M#B;Wh+Qk`>?4&Ik6j(a$7=]%<:#;KpaXI^I0[>)ar`JoXr5uUdn8,+490=UUpp4)'`dJLKU?H)N/8WP\aW@hcLh=Y$]B7:&"He>3%W;4$TJ?!0Xh)k+dJ.I+f]13QSh2$7%@EOh0^]gI4+-Rt4ASKCL`(U1)F]]F1ZeN63?7XU6i>-/cj^09&OU7UA1hA`k\Q3MjE/a6ibK!U'6kO/a?rMQ#/dd#bC_".1H2^>7$*NrLAKTLhI"n6WS*.K@dnT04,UV!`uNH8U+fu2W.`j$1kE(neYWJ"pM^%Yb=CurDlcGK%FgJ[ltPBe52SA_@2@+\^P\I8/^P2u2[!7Lu!g9MoHKWod8$r&!B!A!I&*u3Jq48]o5]&[QV)K2Ld6EFPUXDfid80eiRX?cjf#Xjd'.g3WG0G=L`c%O(IHt?8B%1Ss@!uunqK2!o<6Jl:)o:neln:%@D+R4B,7=6^(CUFfB#r)uuecA$'0fT+"Ng`,GmBm'3*?M@I,O?$0?-N\,cq#&VO<('K9L^k$)RGUC8:R72cm6U&rA*n,dAqJJ.FPR'%BO(@L,99pYT">s6d/*^_`Kd_EAqll.+`;l)@"%%b_FnioYeUV<"1Cp/VJuc^r2ULoOMVTS$e(nTC2RN2`Y2rBl\4>WbJCr*KXo$!0(A*^E.[I(a]@+\8s]iC(tmN&X8$5e8eK_#WupO:g$j+)f>k^Yui]'+`V*RbPoQ8!e=r-^Mt5ZZKKd1/$pTh8u`mqCKf(QO&WfPE%N-IJ:hMe?O%l$7O8>$o\A'0G6`DGVirMl5sQ.p6*VdP<&P5aLeH`_J=9mK\::G^3\h]ei!W%CnI6c2l;5?s(lEaCgEYC%-1K5#2Ldj]J8:hsp%U(Zmi7X%+RW>ZFlGUbSL#PMIF0SWgmUlY!K(b]]V_khWhcK?k1l)]"KTRsVacf8n<&cMK-`[!<`G<[H-4jrnKA5hgYP:56Zq0EUC@'SVU[ZJ\%mPfZT3&S"^,%H\4]nS#GL5gprJi2P=$oR=^c3afb9(#5p^_pTLsl\rUIk:HUV[u\1hLQAf4eZ2P'BaK4.mE1MU3=-83,b'26\4;`,k2n/1D_2RbE7%:?I2]^'!7H6TmQD#<7b%m83tPQ#IRP_"@`q'kIW"-d3pAHA<#$*?1GP>E--I]/,^=&6ZUK:Kna*V@'bAD'6]>nVD1>AIbEE`%n>h*ba'5fL?u_'2`613et=epYlpBlC$'#BQqA*G6qug(aKE!bDQsciYaiT"2%29,b@MU;_gn$@%*gY.]1Eb0B3pA-Z2[!"*[G&LiuA#anr[Cf&2gG!j6?00h`Qk1jZTpPY*-BXO1QjB:K,._JNuQ-r7("&Gj#a*g7V+L;biR(cQ$N\b;STR-S8%HpbU':D>iCRNhhV_YJP:isgdd,I$oN9Iphb(P)#'p0aL)8&hipEY\*Wi?O5!-hmVh'%"Q+F!/E46-[ee<&$_4(?rQ-3RS^W4%4P.F&bbqj[sY_&HgQ&p+/6l('`4J>l4>Z6U%3.ZsL.VZMnIAe0;bb)20!*Ye6chWc"]#,,!Si6".*h77'U`d:38g:C"6iTL"c:!YH!ZGoJ@:J4O.kD<,@7hj4J6#oL^64l,33"N#AbICK9CPULO*(lI\'5o:Ze#pp-`CoXthjPAPj)F.+)1(6\4/@GfN_,_bUNJG;,?2q$%c?KS4hg)J7RGL77K(Tu];VqCs*j1;3s'GZDBPUp.eQ,W[pCZY?^:I".@3_(&M0`:ukdHiSL8C1@oe3tu"!g9m%iUE[2o&V.-Z%?JE,Wj=fJ]Ja_4\uTl^qn:qL?=kRS8MKKhH1r6s)fs=X7nh4g^moW?4@WJI2FnZ=?ujb78WlCB\]9P`c[p1%JEMN>2!l>Uj6#i`mQ-7`WdG(rLVN%"&$h9:l%\91E9n7d>)FsMdmNk0JfJ:3Ti^)&]*Wa8:hn36%H4B`^uscr-P^n31DVD;3[I@048`;%K^kc,micnlQ#j$Tug*[Hl*]]b>'dUjQP\ml4`bi`.0;88r-'P7cTO\W8qF".e()BZ?WY0iiD%BoC#M1Sbg-jHU&N:gCV@Eq#1BR+@\qg8/`q$^6n\;FH3siP6]rCZct&^BqBH:8%r.l&3\s+r;<_:tD&,WiUqY.jM>3)cN1llkX9ekf[`>tTKM5*]JiD=),8eB_#Q(+nm$rZ9i,^N*0.N(4'S+,"8*"EH@<6aI?[3#/0O"1$WSFal[-fFMZ(s2Ej/N^gKjk[K`)R\VU`[PTO)]kMFq8JC[6K5-TU8:rmkXqm-iek!&P6S$(/)8W70*r3OAIb9fk@Wo&>hO^pd

YYpH80gnn1_r,E?]C-/&"sEN$6%>NJHDaaHp;$N)]Mquk(m%#Mnc@eU0!kl>BEWV^Y2[mh.;]ECJ2e+^i1Jh%aF9'c;8apZG!W&Y&UcRCB!sgPJ3>>(RgF?QnR7/[SL\+krg=0Qe1;NROL/V$B$;HarDTdL_b:E#!-*$$pe&:jE^g%UR(5TZAb!QN!2RgDDgam]P-mhiHgr&:\:RE$GgA4VPSuB5?Pg@$+pa:,Jnfe74b.@,UM,r'r<_,RhBX/[gM4o*H[ROE#Ok9PZKu(O4-L*h"OKhd,'UE19"Y\erU8X>e_5A#!<7:1aqblZoJ@3ngV'GSn4:nS#H6Bk(V8cYSs#RMqHMARTG'kp&[@R?2j$A\\>#"XjhKBeO#R"56H!W=N4#R@'4T[-jE+"0Sta8mUO4Sen;LB]oZ#3f._uSH*SnP'i=!7&CiD]#N1)#sQTfSJ&1.t^HVsNWB:2YClaSK=U*:GF39=7!g$fd3/:^U:`X?aMZ@9SAn3NjM>3(UZDb"P9BAlN"KHN6;aCd2%9PV_iW3sM:eHl(Mr6"^4q0t$IA+@D_g`sDt2NMCkM`M3a4_V;4t9k`b*BIW0=#e-p"ilF&RJVgag`po%%-%(.GK0W0"_EXY*/6p"!)F;M1Sd;3`.%CqBp35Ka3sY"Ggd$Ou&>9(h"ULbtM$rB7L?P5=jEpp1l1I3S\pQlJ!TCVQ9_T5rC"^A!l9KG0"'3:i?O&;QP_S=R%k%JO*09ltaaOXdH]5H[\"`!aQ`ml]@.U\/E*QI\VjQ#/0pF)9YNulA,t)?V=j7#9"XP)2L+eaGYbS8Wd)$bu;'8-tS;&GJ:@D43pE+@'r"qM)!VL0#UO@Y'iug@"m\B<7pmI$#@@4S2*7Xc)[+,Ho]I?&LRK^P5$=I5.5JfTJ-5h[Y3D^8VXcapccVbA1E'_T:+0>D`;go,4E`=qY[:Fa!$k5c7LV#8M8L>UC3"TUXJNNEA+qt()Q,lLYBMotHJe3cLJ.`tK8nONTL-R$j!`]Fs+9Whh<'rs]*gBDf"@/d`"XOUMWCk];Nu`rHLWW>Ue.W'7qpm]R-;X,UO6p=5&,P)YhYRSPN;;gk8^Zq1qQKf0F%o?8&.oSE1e:L14=mcX,!g\[1Y+8K@BsLj8[(]Q#Q'YpM@/j(T(hk^k-.#EL#1ifSj4!tUDAe\\0.N2p#_(d7#;Z`)`6XiQI]D*$RKbki"R>hLu%mdIU'GLMFkOP'-4=28"Hu`BO..!0-<:<1gFkL9HK-P>DOFNPhdI%,ZNJG/dsNrDKM'B_9l\'OemYeJ4TD7r='&fM$T4f"T_.<`2Q!udN\]=+3UHV4F2\_P6.XWZ%(a!m^Lr"Rh@R1nD.*Hd\aCF%?a#u_?C#g!JV0Q-:R^Nki-Pg15D*=L=J);p:E].h=Jo^+'`dVto6`biBjrg=[LPH/J?b6=AoC^755p_%j&$t5%\a.[B3"o$.,+E86Q865hA>T@T?;i-7/jFOM[FsOj6)-k`GG+eafHT.PUKdREc50mRS"FRV`C7rPN%8EQI\JlmG,1r^1-DErJR8JM(Z/S?6aA-DT)qIlk]^8ZhAdYUqZ[?A0+T_t+E=D"qb8ILUqu*PD6IRe"fS0fBmskU]Xqmj2aRC(2rbk:ll?Jo,QJdA/.Vg(`(J%D9bZ5lS!66/u"S@`#*A)n7*.1u]eF0b`MeA7G#Z^Vl#'PX+JF!Ink-uZ[UWgDaai5+)UV&.Za_I\Urg.sZ1p:ZD&#,u`&)RJsQUkf=5\Mkjd-?0>','SC5UZ>6f)B$TDj/T[j7"Gl@0FA=6\e/D!u,r$C#e0Cp&rk@eF?72L8ZUKeNfc.Qt7g6'9ahED=goLf8pQ/c81MK4(fo559]kS(V3*l%2MhN[54e-or67W%?dqmrb+^mD$:2(X4pX\'jjG&<2LZ%?c!H^L6Qk*(lAlXf?W"bUUi4*l1YoUsp@HN0S71>28G?3[8[?@3m%hiTJO%W!eH[F[]<-9DLh$h@jX8Zgnog`rk@^X.s1uF1@>JF,7$sq7rJXZ?nA/bT4JnlId@G"biEWL[d\VJLsY.UOpW,([>kq_&I)*2.;+%JW'8l6Mi7Q^mE#GYNg*@U(bDSb*2fq%%!ju0H]Mc#_L-Ohm$olqhs9Yn+9(Mm6;JC0A5F7aG&\lu;`t-Q)8u$.6tkGVbaVd%V3mC$R5#Yd,>eqR7+LL#d\`hF4^[a+u_M``;Qb<8;WmF!I4%@1*^ZOZ[*OmTp>#fU!'mW&L;dp;$$g#(SO^iLsh&oF0FM6i0LZFj>WB"\'0*=(.5,gge1r@CJ@&"4fuFk<:8HVj/#bJZT!oDIS&X6QL$@NW-Ynum[Zgu_;&m>KgNX&La;,%/VV^sI2_3ug)RnU"/>!Un6WGcPt(?L@;?e*\*Cst;28J;k8Q^VciF?I@LX1_Ot,$,?9I(hi7!qF'b#)*BJqa6gi.]j!rW*ca(r.B)WaFg`hPk1gVbBdd#$D-/jTdl*%5gMdJ\ko$]\KAi=6LF:<2-Qe5bKks((IrGRlXhok,[M6ZY5u*5Tian02I';H4H.O=m"0:!k(hU5'T/F)tMP^kGLGXM(%7&))D9=T))E:B`0ZOu::R-rX^.7tcB7n7ZUaU;ZT(ammBa7`@IScF?:?.s2SoG3-'?Ks6(*Bm.TEe5JQ,ddkZB'`Sd-L:oR&#a7H'b6Em,gha5SksqmjZR1,R-,7TP%4aI2bSk'MX`_`[LZSRtg_sRmRu(/^^)P@tW79)j^-)qrSNqVl1++r.Wn(YCh@\C^hoPfOne@RZ%QfNWBOp+piI'DPPZ8-LC[5q"Yi4-*8eoUsTM.(IQq=u9,6"=.^Z^5gIO)>?W-E1*,9h.=MVRqN!Z@XXo!8=bS?9S(05QtR[9)l7"IC6BiU-q^4n7R>:N#;eu3[ZX0!IY5*B>87[1>[ZJH.eUB9JY:gU*DkFSk3FA-(4*QM&G.CFopQa]`Cc`C6cFeS$6DD7ME'6T)sW^J(6o2J'jtkoduHhj(Mr#3gd7l'jI#H/.TGc%Jc@XV`RN7eFb74V"QKc!m(hpJrnRb482:C:1sLM#R:,JPE3f-*Id+655Wd9\p@amPbn5p.K8f^F95hcP75u2*>%(nJs.CjA`uZutD8J+qmRf^T96B(Wf(Z7BX+aeT09bY-BZE-RL;k47DM$J8q&iL!*ra*\9'D@1K5#U6;9thW1"O#d>BS_KrW08[2M!A8'k"1^C#NW6'im%'fJJ#gG^-aKHQmQD[5WejTY1*Va1:NXd%4"):X;^S'C]5NN*&8%3sJsP`e]`@l]7IP?_0CID=1O7^J+Lp(r*7Y`j']u\uAS]-k_mSc(%>Zo**Pugqh-8MA/_Fp<*XG#ggS':]r&B;Ad_8IT)5969g82?2[Q3jn?`npH>%(Hp[bQY-Wlo&4RElfb5#9$M"39.@Fc"<%`rPQP'$TcIK?D!+Qm:.6cqELZG7i.T4gOUN.*L^8Y]23d2@LeiU/f7QlUlJdm1H>T[SB)%#<7204tQ[0lAa_3u:iXRWm'.>$SNVno78,GM_pA2pQ?/Pu4'jJ'#Y>7a4A,E\`lE+V!u-?7Y=f&VGUGK)/t[A7,KbOo7*NPs7\cukLGW[aTs8o:P$3FK1(2q&L85/F8[E+Pamm*/_+NC'oZkU`D"*MS6Sqq!)J,8QUR]7*NVX7POVL6(,mN$i7Z9;5EJ'5bXb@6=c@!dPCl0\.#*>Rb9O@:BC#R1JNaXop-3eI3o.l'@XRR@.]_^RDBBdt[-+G&-F"njPL30F`-Qt%1j$m`c[H)E<&aMSI_13(`dhr4WrA/W7-[&n:n\jr6`5Du9=!f^/$>/6.7TC`*+4krWG([t],IW"BU"025fGR/W,WqMOarmT.TM=7=sY;SJte>NBppB6TW0-8d2IfJ.B/1+,mHKa?SS4($gHKsPQ`U+KC5AH+DVf0RA14U/=[P;'AabM7PK'02C,r1/bm0K3J&/+>GFcO_\.D.,T&[H@QfCW\N!Z$D[s3V;qFpp2VMOD&>@4^41$^f"PC89bl9p6*K%BD$15$3,,+D'lW0'NbjU_6]eRn9tC(1W[AI$=Y6^R_6)^1lX'f5YS/Pnc927$7Ue)R]3En%g78oBSrNNM'bh+DX+2D33!rAJAhh'6/shXhZ9"5/Nud@#$o"X+IAJSmoq\9$GaN<+*q^.EM#Z!?X4R5^`g5ic&3/9DWrCnMF6htQHt;6ZfUp'H%9HmXu''DKefZ*Q]!@,8f@^4EY:-S?%Yr2rkGe6-)V7o?f67YJpKN!W)Qr,pT:5s<0CsLOHO_s8*<@n&q#W0c0qB$(?Df\I<\:>]_a^[6Zh#=IaY@#%TUXlhhJ;HESt-sL*f\pi!5bQ5=49R%K64HlAS2VSl<+CC`/b9FXd>kX/.].FQqX'*ZT@%^!#+QfEo7D9BiMEK4oZdSlsDe^KsPSd9dHj8R#7*rHY87h$kZQ2W:0,&;[]NUhL"+2,/ma6O"G(Of`3M]%8II=@9XG!4s)/jRQX>+?jK?09a2#EG237)%DUmj/ZTu0A8Z!+Z1Xr/VOU4+;b+9Ju9WA;J@7tBOjh^,7$hr#?dHR@*L$o]>1?-B?ur1RrJI<.)6Hb@/gejT*+i6Yj2f,k`=-`Q1c:Ho!@V2S!\2qqHj3&<4R2#C`8b9"gG\H="\Ut9e/oN7>%l<`3HCcB[\CBroU51"1geJmJ[:[E_`dD1$;O,&Q$X27l^<,Dh]c9@J-8aBc0UKg'R%2)4_Yr.Xs]iGbn2dPii\e4,8qsZ]r81M.4[a=b`h7$SsSa2gR9,,]G#S.>\5da,ERI^EA#n/6X6*;oREtG<;6FRW^e_;::QI:WnG#g5OerabA=So-OWq?^+U76W7<5So(G?=;95X2W)5Rr`@4(.mR$sMJ5$^5GZ%0]eipoFJ4?Hk/?3lpQ4`UfjaXU6MT2n+,!rJcPa9>%:>s3GB=[$&'*P*dcEikP<#sC:5I,g178^"6'6J>L@f)&6pdRsN?oi_Z\'eKSJ\I+XJ6YK^CXmSR$b(PM_%3_4p#@3Me3SSiEjH,hIPY!X_qrYmG1K('+?X:0YiE8!J:Sm^m'rQ7?T'.]kWkekWQUt`_S;E+Km\H^cQr'pY*q6G#V;rCPbGnT,c97]Pe9a6iPq].Tn!k9!F(Sg7(([L#.*8=VHd,V\aAMN?H;YoM2k_dctqaK]DU&n&p%>BhpZnaeU`Ae!s;,J`2/S/tWX+*Q>jqTr"I7LUu*/$tgn$_gm^;'DB@RiI`E)X[ADL0r2!o9bJ$=kA`.,#9Fk$XV-Age/+IkLpWTW,"!H/%@r5i1H]rDR#E)qkCn&,dF1*=@'J/:GQM_Uh`4m3`K$D'o]gK_ok<198B-\Ok6F8T\W]?W8^EuK,W.[.&L;ZpW$(JR*Ha5fno7T6/;2F/Kf!]r`DM`H95^0<1J%NYgu.bmoGkr!Z"[(hp[KH>;(V0>4QWE`@g'&X<7W@@d2>[,*>G1rO-M]rK1?O1Ik'V76WU\hPpZGIu!"IGL'=2jc6-%@sT[P26/K6g9EWu3JU.B-TQ'mCQi4\0nG+oCJS?aLff-7I$enqc$5k+$\2H^gUV]&kCpX2g+7%NqbSi:gN1QNS.S=>654O7gqDQA1NK=NZ`.^V$%39X/[VB@l!4WoH%OCXaIWH_Lc9Ui<0-h1i29n@uV>13Lgll,Dr;1`[,9)3Z<,R?.h5Q2lLksKbJg/?u>Z=V4;*T$^NH#"uM=@^>e=a,kbiM<@UuVl5*Nb5E%^=F`r(N_:EOaqPjW'<7G!?]pQTB<-WhM[&\iSAh]fd/-P9$,TTUVfu#["N6p.IB-GuA_#S`Pgs>)c2-2J=lhHf#GS_'(tB>pYWW,Wdp+n!-6`0L:R`eh9`YD/f'GnXNMQH2cO8UU@q4?X;$ifD1]F;Jj*,*R-(ZuLq+\RMK]9tKG+AiRD/g1C+V8OF4k=pO[7,sPZH>#I;hUV+kI=haf.bSiP([uo\6OF/f8FH>u4"t8>O9n2HsJ[$q:?/75l0V+stSl8"KCeA5f@B5'JT$\eF8_QsLdXSu/e*"6K!=G%Wj[+hQm?0Rb4o[D`Nn@l.-m@ofS/[)NLc8fh"[Z)'2_ANN/10ooQIc"=N2mjVqh6"L5#SWg-Ebf*9HE+nXb/kP$>Lar[+2+0cj29j*6?=W'_p>W@)-^/hA,OV,g^$qV%eLe)K('?\PPXA5M\nnnS>it^'V-rV2\pDHa00$,DXYWu/V`HF_i^N2lnTTgjlTn.Z7>#:$P4cEE%LY/&VJPlFG`gdOC9FDF<7e"e0o],*k[B45?!/p#;)^m*hg#X`*As`^W2fr!p=hI*a*';!67b"5@=EGFX#A4?4@apDN086Y;4FlTM3]p^$H@p@bu&adO;VQ*So24+4[,BTiKTlJ::-rle:GA02BUn16I7tm`h#*X"4EV]X5IG`RGM2'3HNFZ':d+E4!8$jIu'^AJd`n(0Y]8;q2PkT6#n+o37/V+1QE$tG`6ANFN,>S7A\5m8qG>u#)F91Uh+8gP+/;>aVVQM[MCK3[529HC,KR:N/"M(s,_=RNEWl&Ds(>9U%>.Wnd&)h$npbbuLBC)#ZJU%MbaPg5\;Wq1@lFAF.OW/>bcRl]J`jX7\I*]mA[2GPo*<8G_5,)@]qN23-sMB@!&^:h3`1i9tOU*=ATO\\H"MHjg`)8!_>bKCW:6R!%(W)?;1KJT:LE"!0:;R5b\42nB-M6SphX!/gST-P2JKs[;'$\bn+cRM*s]*6B0NonFHb[SSjKR$9[f[F_u0Hun0dPqt_k>iShahIZ5`:Q(S$tUc[;b*l*&CfTn/;Ee[L-jU2M6S?Hi(tAt#*XegDf>Qn#7W8pZ\LY*Fk)@7fL5PW$YUA6qABf&.2#*."S(@]HH&RAf'&8O914GXFu17kR>0:b$UC5b)GS-HjGA&SBg"8]rS'd22"?I:]ORN-\Ei,D[#I)"[%*lTM=p.@,<;$9ET/H*:lRF-i_=:d'W5o.k*CjZ9+*$^7I7Laq#Js%<3X\8Yi",NIhdCq*9]MNem_Tk\#akG>k+!JbQ1dRc!F2U^gc1!^+;qO+-9OE#!q^kpOobnDTHZ>u/2gHd,3SEql`DEV>>H\?Omo4+>DBD4$0`GQAeC^>)+k#KQ.h25jK?L&`C-_=Op1SDd+S)5(E32KdI3Y/_/eMj=]uh(Vhup2c]+K26[Y)%+s%;ZZH2U$/;q/PHUOcglAJEL=Ii;EWO&L*=1E=l[tl>i)\2+MJoTdMIp"rml_EH,HMHNk[VG$N>e&g'eqdlbX!q(`.k?`g;^N`H36,=W5(s85XJgFZI&SMn^Q]&+Ut-kg-8_ZU%6&`Pb4cmKcmje4qXrdYeE#H=\n94B-5*e])N5&aJlL+]%b\87JnSHM7fr;`0Z\/JI^rp+dGq8e3+dZTr%C*5LlMF9n8b#r>e8A-c(G8mO!Jf)YaGk&jS'6?4QO_YnA`"!`;45C`jA_3]I5cES0CFBCJ\h%N]XTY'`Gu@SKMZVCE4o9#f+EBrlqN_\'gZ>.:@%VIqS7aW6=V62i6'L,.>DF8/!<,Lbi''#1Ul&)LX3`P;bAE`N1]42UtB"oXm5>(O(:br;u)@Q:N8n@XHDoFg;")`M&6\uQsso%/92Z'g[N:IA>UKC#kA8^H+1ce%a2VlID8:f0SPNY\gQMI!1g-\O6MYsdK9bKliO\(pPA#CpKndQV-nlbta=]#r$>Zin2U)M'69qDbe54q$2:[oR9:NhBap';cM4ECUdLl4fo$io-`m&14<)MMPRn)R/VK&ZqeW[L^Zq*MVLo,1['5Ua.=H+%,mrJ]X2%"'#I`kkO_$'`Z._23NW;Y5oRZS8Tl(q)]6C"iXASfG9%JS7C/_d7^a5T2TF:I3!sm1k=9R-hKX9i`8YJN8MY4SQn"1)3t,ho*k#Nn`^da2<3$P%AP$i\ngL5\a'nXqY$!K%RZ((]#,^mE>$M%);;aWJpu#jaO08u&rEpu='8`?;Dg@O3LheRf@I/IXRU3+?0n,-&#K\Z&"K9SZ&P\u4rFS6e+eQ6bD@(TSZQ0ttiW&*7#iZG_@2I\nEV_8/K9o(a&bm0V*bH2Gs\TJ*?`@ptKk#07Ei3&6JJ=7q8JZ6*JRU#P50:(M+DIOBd9*#%>/08*#hP@+qKM)7C\I,&SE6B2r0qD^`n;K@bueSFl_c-/?)2?9bD"q_h`f>P42u03C3S4c$)>N8rU:L0&euA[KSrd06IE(M7d*(^$#NL6'beT5*Z(NC?8coId(!CVJA_HY@5:35qFi!Ri9?*N%*E+;Z"0;R+cqSJk^kRJV+tL3UBHAD^cdht&UH4++Ei@*JlSWWKqZ<9^_D>ITTF*mk([9'dP2"RNahVu1^U5+!'.hALN,`M=F2:10_1DQro-82H''mI9hnu:[QK=2`#s=5d$b46"38%6Pit)3@ZTd?bss0a`"O`!Wbe/,6D2F/`ZS(s@b4=d\O]$cXHdBE^t0X@g2XOi"uZ4mNG$X6aT-(&LM.>:S?KbENu+X-oulEC&[aJEYM^:C`!ENgA1=R)1Re<#a,ld]#$ci1*Q8c_qb6eq%t2_F]\(:3BK#ra]^NK@\:9Qp`*$]J0/f,.S!kNFbDQ10:oh$CiL`HX`C]^Q'b>?'-4'>+#n)ohD6PO.i>WNP(K;*oG/7?@ciWjP^&Y]8`&VuREZ':t-kpUa`P@8[TbLtVZ-:q1@iCoEF4NOOn!V`0nksgod@?T-U9Kr!,`WP)GKG4SY"Nq@-6:qJng&gr_M"%GJI@r9>ZVV26,VG$F`UIMkH%oc;3p>!_e3j]VPa'N_`(7gDfUOV7d0hYA.-j>)XQ^_#Va=ltRGJSBTJI/-/V%0Ro-Qcdl[e(mfc*i;Pf@k0#6OF!UamT`Nu=d(*T*L#gH=XqhPX^mnE5$1X.&dcI;4!NuLT[P<>8W_Uro7/a9kZ_H;?N'9K7\9B;0J0Oh_]CReg,Fg@.K.*Zs?9]Wc)F\Wj#LoXgD;.1X/iPbI4FR"dr[%4_NeV5#/u_F_(T-urk-+qu.Q0Vb9C69TanZ!aBr.'Ja5.T_$?&$a2``"(p)1S`0GX%Kup^&\7$q!F]#i]6T#Hqtp>/lk]*s+Mga6:(rW'\q)RMA.^+fC,AkB.Nfr2pRe.8N!Gpl1C8RX9N[CRfOA%V6g@F++LhW>/gV?k!YMqVct'%Hm-#l1g=eYnET:ou$43Lp-((>4[]iJ*2+5CsX;O2cgUsc%cpm&3@4kid75Y`[;!dCb(c@C0fl^D.7=r>h(gKa5X$VZ*.,UF28&+BUCIBD:A7J"5E]O5u-[)=1lG[?>e1ZXe_MfO!UJNZ_e:qFbDogl&A>\(G29\K)C-h]J[D;QXF[Y453eoPRPQn+5d5@:BBEg8d9a#imA+k?NVn6GBlGRGt(I&)Ni)MHqsjCLH%sS$X1>99hI/P0M)g@3'qa$QO.-nk:0gKlF<3D7'NpYfj22?d"1k\gs1TrPsZ*hJNBJ^;O?unOV1LMG(QY:$D'Uu.&$u].)*ta<\/nm'kHof*h;G[WYj_C5GqhsKTDRuT)?`=,rLNl&eI+>55WLfJ_7^A4mDH-Eb.US'RWlt!V*r-@-,ZJs,9DH=Gs?jP4N$E>N;:EVohF^/AoW)&gEWV#FD&9&Ir=+*mimjL]OcYR*_[0"d3.Y\"tW6J1#g8T0cj[iBs"1)O`]SPO.)6>1S`[f/B81@-^9)l#hH&kUX-24,+=>"sG%Y+WmN5rfRffB(]^@gsjGR-P=UsgjXD+?kN_GO[-9PhX#P2rpH$LH:0D=H;PWcV7UWfP\+V[=BXds)d'NWSfJmPQ4V``oY(HFcKdZcYL'E7T=%CVigW.JBmI$!aeGc%0enK2NPd(TVOIoEO\*l?iXEN,Np5H7li\c#%m'iP"\.*MKMC$Nr0@pGjY4K]PX33S<9`Go2]X.*)Y,3IZEIo_krQN]1H;@E,2O*R>3P2ie%(c=Nfh=!*a(4$QL8GMG1D88t0mr44kEmK$+s5Z\AK@c#*M!rKq04`c?s.>4W5p\3HAleko01d!/+<6NQl22(O0/\:Se`H*rqJt",5Y0[?Zs`Mo,Sq\*2OUHVbbS58E3$frg6!E'R16)Gp*!pSq&2`L^/.]oB-4lKD5qq"XGB:2+.KW*rLH.68iiqY$+)<#I"DT^&@Mr8oEI0#AQCn<&sljG.YOOed#]0(&UO3kK#OOYJ.ZSVJ!D49FPBnUJd-k%`aD[Yf^&f))-7$ZVRX=:cSW+3YFF1k&SD0c0"#,r_Q!ErVRBF"\))(Gt9bD4g7XV9*NiQ+_K5C\1C`D(oK=XR-Dh`GOW#BmAJ]CFhS\iI1#ntTNDENe%[$k;B<'5%DY?XtX"PA>f`RHup>pXdPOq*=!U^1KS\SQ!ffS*f^TTp$+7<%6'^r@BspiN!*-FYti75ENJLJPGQ"B4Fbm[XCsQL#/T:mDI&O^iXrl4GkQC;880>[&,f&L-3Z5T\,7VMEb`QnAEr#Uk9tZI7GqQO^fUB&=HS'7j$q8S)Qb7>cr+\[#4'l:=&#("KV@E)J9V2['1rEO"9R!S6F7*9!>$Og5n&SQ.!-8%VtL71,n^c_8]98`LbMXm"Ysb;'7lm7M5l7R]/*P/5T8W%%+###!D&1O2!G.kQluE$`.oiC&4u6k;$^S60VAuNd4eAt5Tqn9ar#]]R2o*^Pdo?I?r?QHG&.>glg[#\pmhTf8t&he6o"bhOSSrA:VArD!Q?.[+6FCb@ph#?Ut-Z31=_Nd#X[0*@iYTu'NHVf\hE/GR"?.Xkl"'42j'D5gO[,E_#DY,kM>>d3'5M@.h0TY8WTa/a5r7I9$N#:*ZX(++SDFtbn@V-OAj4uH-s6L$rf9TBs^E?(`UUfRlaSWL9ksW)3/R57s+*@cPjJOM?QEF)c$@ouqXaXTrmFWGU-R8SK;4eKP5H2Ei7FsNOQ`0^)u"5:H[TB\Zl]^p#_jf8Aca)$!Y=:k(a)ADW.KSn-%q'gjWJ"(PD'S]P6;T<1/'Dr/5fiUaRM<_Y:mX<:4UIh+ijt39qamZ9u3T9hWR2?[Y*B\at4C?$Y9E^%bCDh!X:=?7=Y$>O9$k[?-sF=qoI9:ofd;;-kqMSX^[>f!@'ZWm.\@eSE)ZInJm`qcVMFgL]]q%\FBr.OIiW@<&a%-CFU1D&Sh]T`ICnRdVBTu12:t2uf]3o,J&VQ#K"nQk0JcM)kM%$W]6jU;N9bT9K[2D&d7j.B`2h2qXcNdM;,)jVEEL*==1@C<,e3J[s22eTWb&)muUW1).#uB5cg[Tjk#r54?!ON%mk42[e4`V&QE[:aPF\7@jeR1Q?EO;e7WZUWDF=MlO@i4\lI<&d2>/FU5@WA,#1_$3jOs$=mD-LK6rSSOqq&"C@d4A]4&XiuE!JR%P2_<>XodU$)W$!2"dfF-`(jQ0&#"j@7ZtrDis"9H&b8^#pm14oh/18E0Yo3($pTd5a#TAs05C[;/^s%,;fWuH';AkGSGN:4G5bh?1+=%4Ok95u]-e1GV<_<3&.fF>8r5tK'U9A05p7dU+Z..D7&1Hj8M*b="@Of^1FWG-,_P0/TP:*o,Sknsi=k;L&%>\8#;VYDG_e`l!,;E31mniD[gh0o:P]6omiX"YBb^D[+V\TiM-<.eKd6p,qnI*[.V_RPS3u:_NJ'ZZM@"lepBVk'(b\D2&hPr7PQ_?r,uP:B#L4==Y`fJo+Z(o''dekBP#*i*8BE=q,XbR):%V:(]b?I6+Nb()K/R4MKA:f.g^gUmprID%O.gq]!'kQ`$V0PJc%4eb&oiYSegJ_+h,ILm&5"gf-A2b7rn\.K`8@^p%[ck5DX%g!7Z^'XOJuWOR7&n,!#On9Cg).%V@5jWam7XkNPP5PMF-9s"63F$B7bQ5$V5#N,WdWJc"uBPr(UTI!+Q*C9[jBX0an-.&Z:Z\GB\.*-4Rus!PqQE&dd%8S%c\R6Vr!$U0qBa6NY^&H5IX>F#b!SUIoPF=WOC!*C(;!Z?IM_ha_tORmn#AeXcAr?rmYbV1'*,lk#NV?m&o**8,R?Dj&%k]L<2'[$d4+h4"057KR:r*[&&L_1d_?%D0d"$le'[f^0.#gN1IXo6<@gR4'V_r7p#]JKG3uUC+gD)QUOCt^E8)DKH1fS`C;RtqUdV0Vh,nd!/sGBOj^q_Kh*fLI_6Kja>Eghi@[^;!1*R,FckS#I`Bt6'?!KV/r%A]ngM(MZ0M--O1jeI:lMA#Vm@tMd9PU+Z`.hB6X=!`?rs&.@?d[j#_kIDYCg2;N^%N\gR"Gm7Tp9ahd2O)q2f9k!k?=^T@76pCZUB1[5pRZXMaU*W6fl6+(geYPp];Jp`Of9uSmd'CNc,#Y&)doQZW%a/%^_pF#LZ")F87fC\)q/jW,Ci\p\&Ko2#LiQPRIU,`KLT@eg`?+cZ%=+W);@-$j0?L!lbqhY7X3Dr"AGHWq?#&>">L5Y(f"g,bYN,6/re$Ts-Zd75c;i0JGjd_M8Z)onQ:lIc%:]HP[QmIH_HB"Y[Y=%=O3QZ5\e@+80+lQh%/YjXRrL>1]+JZR)?;Bnc<5#4C0ORh3T$fHFZb]KE,7*'iD>l.\?'13nC*C]L.fQ`PNKdVPMI5,SCDH3/t"=F(VR^W3nCMpDat'#Lo]@K9&<P_T6*P1#s?^KATXT)bphKp9N>EPaQV^qRPhaEu'.fs6dCJC#B=Q':pE@*KU<#V4(`EIs;[Rr!`o7=R$OQd#8.5%K[?&4%^YNUjS@m!RmR?hr4:F0HKd[oL'7D.r+1FI%T0(dcM*%,]M;C)KH"=I#i'>(K/\^o"85JNHt4=YAi[Hn#Z!V$%"n_%pN<:GRpIV;CmL7i&ZlY%?T#*s+6$.5UqLpYg.7.Q2ea@BgY`(3_#`D$I=%'hT57*e7T3>fS_K)un-qJ\Pr,OF85+[H\s'd,k;E=!=ORg[#(#DXqXka_b5//RbFhi&(=6\]tu$$IH8fOGJd(de]HFUZi(K+&SfGcJk'O">ok6hNi77j!GC3.MI5K[IAC;T^;b,=ou?+tjmL6)+Xgjrd^DF/fs)#hm`5g1\WnT`r\5BhkOc[8KSs)+$0qN*tHE\`7PS6mP<"S$M6lBJ@a9clX@OT"Z=i8#$m,\LuR=G4TdPJrpaHk>YhLB!.*#*(E6(`7#OF8;:*soGQ]M2N*/g^n\251$cG+8]=sNc$]u]:C@E\fVcCeK@pWpjlJu=^dHU@_FD?R+fSb<(^Ht\6fSJE[tjOsT8*Jj".nA4;T=?p>UI%LA=AV$Jf0.i=pejc1CPASlq`+,JULc(fRKuocf&5naWK%`d#4b,C`A.KU)MBZ/+(a&oo>u_MKf8f.7D?7+YZdjOr9(5$ckP\.:Doq9=Fr+*WKp+)/RUn`$)#`Z:F#s,ONg@M9J=(jjMLCM$>\t>^!*+%/f.r%4b[?#(ltTXfjAB/$MSS-1bPZ/32<:26rCV@F68c-Q_I4/8I$C4"]%f+'7rL4ME5T&"%6OoeO`6Q[e5-bpTn.?cd[J-*5l:&Wl/SDjh\CU#Y7q"PB6TZM;d)=cY\TMW&l>P6g2LV,_=c^]OYK45m+V>JlI<"$"[Kg,UJ_+lqIf5%\Q/#Ul7lj6Rag,loZMm0REHC#Ec'\`Gb[Df)AO2INq+$-u:&.'9Jt9CoOHX-kk\4-S_>)99"b%S+9LOf86#b5HCW6BUYAu.pn"Z4P#Q?S8-.^)tfAJcRRYS]/lsr35%^2pfOK0=,VDAI21X%[[CG]jd@Id.[AobEDQ>#S>tlBe_O(d_ccM/'X/-WSZ<5'%TRDMqjb&a'.TVZkE4D82K'pRqT"sI*>?Ti)DHmfB:!@KfI[t0N`1V)SVDuj6$#(.3f8-3oMA9Y(9Jb?K"X"HQ?ZN.X5#/04S@A)[QEWN'm;n3TR$#1!TfY7O3^nT*L8gF)s1*dn$k57+F]G4nBQeC`dBScDiuf1k-b"CL,ba`P4m@/QOf)LdIM9KHf]WDcE^4q'T7r2.M:C/\Y\lhM&@:>.@L;-%HD$j;0(8P^P#=CIS!^15XFg^9XWmnl<,R7HL&=!dSp5[a=GNg*(r:D^Dn_>-W<0R4\r:OaWK3i%JL3afSW)V1+'+qUWU"OR4f'WU*o*`ZQM>Z;Eb<(%io$G^;-_*CD^56>r-GfZRf6b8aEu?T")H]+"uC$EJHQOmPZ+8\DGm_N7KUf1%](+A1>H$^B%/6@0[IPGJbaq7l]j_75X`Cf*/A'/:KS(^E.5-g+:%I*JHS4B-a_CQ8ls4n"'\HXk[R\$LclPFZ9>*mOJ3`6'_;kHpA4^!Y3)3uZPEcu1J&XT2trm#4>gW+pMnC=WC0YJb0t,(4>4$mcJ3U$br];FL@-k-Fj?gXmq/;,f@9X&mde.Z0-JFg#E+aP9/__Gfr$\M.)LaTA0";so$Ijs&_4`6_-WRX0*%=.X]lbSq4X(1#@r:^_t6![3L=BRbq!@JYK#J*d3PIufToPGLS7l51PG1CXIP:m8[_84q4Fr>D@Up=,jV+IZ:t_P%N0A\s^IaT""cp=TVAY@6u6M6;5ZU<>/iY$EcK=DY1Bh!<&Bof.dE8btJ',Jb^k%u$'\!qRNG8KUl!MnFk!b)EcV_@6$^OMR+r1D+7sfGf"@3#>\%XHGC4em()pGj+0g)l]9fd)f)>nN6euLD#.fSEmW$^LsP@CZ;>k1&,>`6FiaaoIa@&kl*c)*oj@6Ka:LtL9d\,TrHd3`1WP&1a!P.IN>q)B'fJ-#:7d_1"fetS/Co5*F5^(Y?G>obe)*Y=XKB+@$*p?&G7Lk'd=?b;mD.%%$XRZN=4/=,uO1%E[<1QK<>EHhiKmrg;:Mur%[1LZ>a6Va]jcBj#n_4mLH^9`.8HS,lndc5>"'/%^^[%!#'^,'YQoU^kJ0!5OWI^hKOnX0Ns@RWfD4cciUd6U,L\"Uj&fQVn^./slW*Y[a#g]t7UA!-:UfaS4OI)8PY!E@E5"s'n@geR#"KXG9NUn^p]+4s#75"A4sL,/ro556A52rj001?ue!A**muVDs?CBe[S)'f\"%U""38%Crh:b;IN`5:-qu%YnM*Zlj'<"s1?(Z.5eD?#%S96)?2kL1O2N:Z%,b3$O[-$+ai&M65K;9Vjd('"8ihSAYUMOTpXG'.->*7`O47EefE]-od^#BMM7'-o?S?D<]tXD!a.7Z[ge&infL5eep#UL'gUd7i67m[Y'g;EE@@'.j[!K/?*2SYP*mO[u(mKMGt/Nj(5EQ9-I(&5.p[RXYb>Ok8i=5[aEHK2b3(;>Lr86mbg0-:RU'/7XAVJJ6GqaWJ+:TU5:Hf:dP4a7G9Y'3MU$=hKm4g2M]dDr"F0#iYY]BL8@[;0gV!Pq,'%)2l_g0qNhL>o60I0]_!uRC1<>DCb-igJiE8Aj%d7+2,T+J`fb^65AS=G'mVQm0(,g?9Pm7TgQ?eg@HGo%T>BW1>)HIp2A8eH_[hL0/U\[>3/-m>t-WcXFqPYi`W7f%t_Q)?4FnVM??>nbPjA$?B3Yd-U2pn+`uQbQFudmJd2gk[2#OOd+"HK6fK)!ARAdo=b4I9cHqj3r`an)pf')eQJa48]?nU#k?1%,R#/.4/"r1c#eUAkt<.GEN]O@.1%$,##ona>rJ/7(^D"U+e`5INKt$8X0_n$0bE[7^&872sCDHa`n5OP7@pKN3"e\1Cl:>Yh?F\r2;>G_,H>@$afVVmfQt2)@8rf,"f^!:!'(Mb#5QAS/qOh"/JfC;XMu@a$_7,,1U<8'2%]p5$E`a,^?8QZc%pkQkp6UDccBM*jX$V\rdXG:Nd%[`0mU.=s?V'M80&k+>Iu;UeDT*tj$TYu@:Nd60esa]/$;k[aMjrMLOQB?*&`3jr`9PJn"4mJoWC:uY&8_[m.6@R1;&_MTmRU?q5Y*;"5[!ukaSi$t.U_!X;.#>eD33ohoXWX[V`,Z8b&1FsB-2D[[]7QJQ^,p6)2S6JcZ6,c:!OOicZA2one'T>Tu"UPTo"K5]N!76sp$nrp^5D?$[DT7e`m:@PXU7N5s3V4.6;_hek#$bdC+>41]R?o#IJVadVPfj/gXSl&ua%AX:$j!k1YXjTr^O0jS-+L@Im/S"9LXI4H!C8t-V%fjC"@,C@OMXj_6j+/(9gD)lT;lp4,%>+%(m-*k$47W%WE,7`(hOTtJg$9a@?HpbQ6;t)@#0\3Ja\5r&(<$:LNk*R"U]BHQj+m4N";\9rA_;\G7msTr6D$/]a+af'aSrY@Ye23-n'lUM@)m$LkN[*Qgi;hV\a1pZn)!6[Yl7epR!!kO]l")O&p+-O)#tjo^%>u9hPcUKGmWR5+g>j&.0J81-`_&<'-l$AC&aN<`'tcr,GA`=Q&u4NiO/L-,PZgtWO='s#^5D"Sm-i>Z!LRP4Y*k"_@7L;0>s-p,g<=@1[N",=cej-aX&aZ?q4V,C^/66i^g..Sd\Cr3L.X4/M#A%+OZ\'F,A%[4q4j@Wfl6ks:\9cg=SXoXInJrQ&$p*%68!2s(9&=ZH#(NH.GGfbeoXMhS+qs^WMk#kTZCFeO5uD2K/'7LE4S[EI44RbfM[P0_@KNqICp@j[4mD"&S^q91T[5)Ai5X'hbGq9I0a$[?KTrLC,5n0eF#CH^fHcoOS%M?%uI7FRD1"%#lnGa:r`.4P#*lZoSU.;2@%sEOd-$#`(Uk@mLf*"a&e5lC8H]_nC$c3ALhKaaqN;#IMSH][&`"B%X'Yps$L__Ne:luk/+C$76_IE^4e`=X,k2c"sr\t+5L>!i/kR7NSihQl"&J1s?:67XaVa4SW2Y7Oe_5W$4PR**q_/5u.1$`nCos1QQmp8R$j9'F&d,^`07'C^gVY`>Tg0"2d$l\)p2%[c?pA*eEOLTG-UGdZC8ZJ59HC:_42U50daZZEGG"]&S%u5B4)Hl-sM0YEYMBqID;1L`ir68G-OXOs>k]D'd[p[Ym_M`3^F@AGTgrQXU#P/0k)`j9D=GN'$-!.bmOp;,ep8/FN=S&ZkDb9cK"C0KA#:ru[Qfc\U%j\d%e2&VnuDup>ucl^pBT9CGu@/#B_&#Ac'5VER6Qu:4m5K37oP?94t(qmd@gNn7]YCEu__kdhO`@0"iZ7$\!1GnKhNFp8t"$TR=W@B,kdsT#l658&C=)h3!Io:3=;rH4rat,+@FNP?@Ztqoj[m2r_aReP!H>Z$tQ%n?N`O]'-tX1`3@WD,0^Zj0MDQWJjf.6U!lF;'G+!DO8E$:d,glsjl,.XYlTCqe5InA&Ch'<&CNrd^i:R'AjsMVW+\H?"V=U90PPm(L\;aA*[D-p+TqlM$?oo>:j!6gJ[2d0p:-/+4:o:Motl88O5:2uWVrD;hOcKP.8UOXLVb6Cf3io4XsgBFLD&pmf.33o:Fs0A85!;&;:TTYj@"&c_MX1+GQ+f]Tdsm.Z.3!U&K!,>b3=Poo-jDUY!#+p7VU3I6_jT%Q="%J>\;66AE?Jh72R.n$8Y/iU.rO]T#)HBcV`biN:&ko&L&>KL/R*$*"Sj80EKu#a$;2,$:*tO@!I_OED0BH!*?q!t'5&!Nscba&]:SUj-L:fnpK(/DqXl#Dl:S,^LL=[*l4=(5$f_g))/6[7)dXJV2`SD)uWpbGZ7?K>F/GA<)2Q0HP55l/OK-W7qur+V`XF."G$bg*lD?=[\)tPNaZjL3$_8G1N6#WZEO96al;%cZdFEmom*\3TgYcd'p,m[mf4<_P&aEf)Q3\OS;l4'G1JGDI*h\]H7+0SEDU&N_/WHO8hhE5+lmLXm<&g2'WS>?4Ek[sfBBf68=crFD['EZ'L,>=e/*Pc.Hk@0P$b'g8Q?=85L?\CRNf@U>,6_Gm\J^a\lP2rW",e_fD3'mGmJLGi)DFUY>p$k^8AK@WYZ-GH^!-=s\5q["?h*hbrkH<`*odgYGpf^Y^-UER;6nj%"np-#8O3t6I'+4KMPWg>5Bhbs*9S;b=*kbk'8Si^8*hM"4r>'_[kup[mCa&H2$d>mAZ1B"K/Hnh%Gp\7oWS@$^,h!s0l]p10Na(hGOpUWtPg=YME-hKc\!g9.OXS5s.0ln:J[*EtnTT`Y/<1iKE*3jI%LPah#_ZT:qlKkd,1bI*gu'gc)_[*7$9gQKK7JA\/*$C\n+J&V\0#(88$dm/.ai"8U),,o)"J$$M-(mEVFYke)k/#A%oN/qp5`/1*N)-/7-i\SeeSf+^`e->l@#8QTN!'UBHVWB;(5X==rneA3?&"P$IT4l*n.m86\D%SHt`r'fHk\\^?jHXO01sQB%MHChm!)UVl2bp4j'K(KH?3#p-THWhKYOa(ia[3)jqUG&H8t#4!odE!9]l?'QOd*SZ3RComAs`rn$L%m6f"VIF16rPaC?[P/u!0@tXR+kd6(1paE-l_QnUCWl@^H/DR*`':tcZYTQNq*%uLQ&aUmJg[/*U?4,u@3o>NA]XYa\.e0_W!>XgiqWGi1qH8N(K90snHX9.5B=<>69@9P"H,V'?:EW_=ZU%\"OnQZ#4aX37=S-hq@;BSt+A:I*6sT7U%%/;H6LBK9dBA)!VR@lQ#N[GAh!dDh'Wo3Tk)`1iHJ-N1pMa11p6<_EJL'&UGJ'ND#/Ltm@%?Jjq#05)`iUA$gIOQ'DUpB,cL=DDR(HETXms;_or*F2%kRuBQOdmWZ)Ug6u[RghHl4H>I\@GPlRSWDfufRA@X[+@47q?b*"2i)Uo((Rugp1qQdK^Q;ThOLI3pW#T7],N>TPofL+g-I0UFVqoeiJ"*;.Md4^5P@SJt?3['NF`m/f"*>8+O_s@oM,1b4>.9X`o#&Zu-g?>B0)^1SN+mL^Oi7&XLZ3^QJi.8X]Qc*;(+(lbDrD36O1X8@bcaGgL6"1+0%8%.Ybj.G3"+gf2YR['YcgHhAFKF5h[M6VHE46Fu?Z=$K]9sc^FL^YR53(p>Pt9uJ-ldcd(n-E[\D]/\0H)k;"&UjMM[T4jgnu0etZg*7,Atr'slZ0Hn.r5]N@jWPK@s9`%;"a@b(h@6T3X\f`^08Yra&dgN9cO\gfF%r-m3f7NH2=&O,qO#D@'IU,OnQG?D6-0>TIiV`QBj@F+`6!U(P`]+O]t;nF#Y%P>)!cdf*u@am+pd$,J:X,CX&P?)UjpG)S]PV/S.=5]af$N(rrU*a\5rEC3N-H^N5jLr@$WMjOf-"TE*X:dR@TbG-kho6U9\,jMUmIFnq(;AEoeU$D$"t1-:7MZFG3A5kH=H8Z@'_+%]d`'X8"FJoK$)TWk\Jm%`>iRHiYH>^*SI5DaREolZ1>WSuDkR<:c6-]InAX`9W&D45IYnE,P*,9b/"eBO8Sqd^uK=oKLc)Lk8:9N'gb$/P6%oBndc2*cgt":upYP+\0T13`ZtrRjacr(g4'0G4KoU[&Qth$0=VX(%`N&l8;%$O0h9SUfcPe1!TiBiYfWnZ1CR905i[,1Rt#;tDa0a7#MIJpk4slui4q<*\EY*_TQq*#r7OVKp^.PX84I^h5j!m7Uis;a2u/1Q9$@!a[LZ_Q@17t\R&[M:;Z\)'Z=PZs'M5VcYSFd$@n__r"eCA=,%2<+.U%+:h+m/=`dS@`!mfRc)&/16O)ETu^Z7=J0L?c9?ljmq"*IHOBL*it%%N.h0h4J3U#(@]GU`1@%HqIt)l)N8JTCR<@OK/\s*=Vpn\O*)2X+eDc]Y;,=0/,.d!^PJS,MEFMMSrPMK/ORjMo3!!0_*50?;`^T^KuL.39'+^cT7.-oW5#HXMFqrnCdqL#cU";.B\cn:0g+C'=^J%$$G.l:=)cAbJBo>L6@T3LJ1-jE$2T^Kq)E+uH#1"WkrY5PtfJ6,Qf";ffkM,c?S]A_'$OuE#Rkim:6Q6I8sG20IM>JL_^=I'-Fb,S;=h]JK@kR-$6l45RVF"0[OD]n9*6n:a]2@-8Mrn(,st6<5nl^c1fP2E'%8ZMN2oBH79(c;1^+<+eP*n]9N=La:dYm"9`[YD'#bSb0a(MN3e!,[WE51>e>,N&%tc9<$3G_3j)XH#'&!m]F^jQ.22f9(:eMmk/Rbai>6"hc[I7EfBSeH*k'ADJ3G7*(EY9`M`X:Zkn.(2A3XQ6)Cr>/C(4ZZGM2Pl*(,*8i^C8iF_;\3Oh-.HDGroMeFQ^f_DJsDW'LCp-F(NZ]iBC)kAoP"ZEr'1q_R&en=hj=Lb.fZ^]7,a0Ar'#>B=%JO6d9WQa.D,F,rR,*`6l_@D&`lJKe(A8+'q?!A#0laH7X5`REUQM4:6n2T6=u'ZYb/XQ23/*_*aZC82c=3l[>5iOb%PM:"c.OMfIJm.J;bJ`+OB#!3c6"K+g*GFic^Yr7T=Gn;P?qhrrt%Ye\:QK(]I`//bU17,r>BHtYKj>\4:%HQ!3QIN2OH=Uio'LlB&S#D-X1C.SD@OPrT$&/q-YoLDBLb%u^^i'D/j3e]I'%feq0kLMSV4`Zs-3UX]_ZIcnLo'Q/#&cd8#WkZ/(-;Wg]Zh-s6g>2<_7,3]W!rjTU%gVlnnC]7`ql(?X0M+-RY0XJ(=TN7an!_fCsNW2MmE]\JsR,,RPiMdG=f(TTj#FW5U%V^(K=0/`oTGRg<#Hup]CU&-94m!\.2&hYZX"IkJ)!,I[@%a)tb]s$PNOG>SkbJ2&/bQDtjmHL1^40Tt&qd58cS&Ha:W!)kTd&<%GfIt!/`"7#"R*"uETnGe^.q:[M`?8+rY*G(IsU3N+Dbu[-TgLf'[fqBP]7HtO!Th,;V*6(K@G_4"T/,7:#VLA"-Dp4XPQt@I,c/g(:347_R$$uFn@(XJEIi!=+SNa-;U#T@N`ub0J-KEB,D%^QZU"SZoF6(ftiHEW$h8^#ln5sl%)X!&`*:R[HG=Q,^3S]B*4AN#i1_=DVWh.`,B*LV<_2_"rFDG![N1[*P%Y]p`??d>)+J5N2G:YN]bSVIQKPMJRY212/8Re#GbGHuW/1%NR%@:UZaS.W5P+*JYAY>pc`'fKK"+tL)bu(ei*N(mCON`V6b;+\OUg7GqJg1qH(W]S(G-H@DX%Ka`M[Y+>.d5f<'>2lUJ`H-\&I]0l:U5/MeUd!^eWLkgU8d,?=DJ29r*P[)WpLA1J!Q",]1Tqmq1KH=Kku(En#6UQB0L_;f1Lj9caj^7]1C.d1)&';#Kq57!gBZDY2ro_%$eb8L^Z)USpbH'77K]8`d(/KII/gq7((#!LP_oVM+O$??;`5"eB4Q[:Ch2Z.tS?LfQ%K@cdX9JWt\[?<\^f]+IrI!YFoiU2;n^"J_;3nmFlZt;5Aj?=u6Kgpl#=c3G^,=!1Ga:[1rX?W0fTuTTDo=g_d*11n=AXI(P@[S&d:d8(A-/!9"6]"PB>GBPE+KX/4k'W11k[rSIj!LhRDe+?KR73Hs8;KMt;G7j@%.iA_l(RYhB:+K-K?9Fi.^OBq,oGr1Tr$A_TM]cB3;g"*]hn=QorHH0DmSfl*KJI[c(333g[&fU0Yg96rFW#uM?TMltA+\o<0:Dc3#U4=4)TAY1mr6gg+0a^a\,W3QrVeYq?Rl!DP,ON#bmIl3lLmSM(3Y<5i"ofnNM3=#OV5BW;-jYN9QmimkJ7N@_TVNK98-/Qtr8@kI_:'9[:;`mW6+.dsgb`te`0c)7CPdBTH93s,BfZa&A@)Q#K%AKBB8d\0NH1IHSEf\b(:J8n0&\dd+sV;(6Li.](`>^b7[$AkH`TMd`h1,@E44*'o,73lC!MS7isjRHf.7oSMQ;BNF[\6nq*'/JCJm3&Uj\T-cZ0`-'%t+HF#mic>cWED7;1N[fC>L1hVPYYn.o:.0q-Td^^d-/k+8866G3&4*E'^[GLEnAeAf#;>!3174>T*.U@:c6epkdu.RRQ>JE6Z?+#sZ`I_3AHrdDNUZ)O6Pr2Xj'SFIeDk((a`lN$%V>&%994\$RiKW&iDZad??K\YCq?_]&=C&"aho&KENh_'#FagMlGT^n=_@E$lQp*Pj:&I_o4kg!C;e^lir\t\DD*&qp"U%72%]m$l,nqb6$7jQ;j6(!Hde_;b3Q;j[j:A%X]2C.Wl8'E3p2BJ[_g?Z[Y;RmeMp64Z"l)*2e%s[B&s5T@haLh'HLsgg01dFl60N!O/EoBBtm5Pcd*p7XW,0AcB_[P1iMfU[rn]9(e)Im=gH!s#lt&*V's$oMq,YkSMS=,qo93n_n[McVTcf$f]I`!P=kCXDXs88KEO@C,n]FP!dhp!?M^J_Ypu)%;LKIJJ3omB*]:W-RgKgr,lT/[U/)3@D)Ut\Ummkb/pGuulpHn[$R!Z1.8_YEl3s:F=5@A,?7J/?_EZGp0UR1T1S@W6u3^[nI96=3%la)A_f$Qt-L<*HD_['L2;!FP^NnMu)1e)2[P_Y1g1t3cf6P`.+'2eSC8JV]Iq%R+enN%EtJc!DmaS?65YqQ,hTbJS70po_#R^:Zf!o\0ECajf1GUKMFb(G=@mA&fuSO&!t4QuTGdb4R8kZb9N*,TH,dQgJbTD#=Y4U(e&"\2L+gTtt:aCeVAapaQAPQe/Y=CRRX17l"6eRsu^+4rK,VSCI6VZ$"Bb?QCGgB1+.r5<&Blmc/5&N7AVAEJ0GUojq)../H>#;M:;@fJVFIj/4]P&Z1]Pq-aW<@'3S$ZZe"Gb=OB^a'iPn;krMSHu4IMB;5pu;2Y_7>:A$k(k#VC24haV7ad9;<(5ZWF]BYa_ATp6Y^<#!Wn%#$Nk8qbX!`^,IQd/E`jO%Fc.L>1%rFN!!tA*0BqDk)AMTd'^l%bJ/e$?pg@RZU7e?HP)I;Qql*Z>Bm5$S.F#XeWR?cqj9'qCZZ]^p?Zl3+e['/#rV]*9iBf:+E6[[[3]LCkVadsp#fj9>7"p0(9"B>0Z\RI;9,*qf1A,AL8+o.Mn(,tUlnIZ,dAF(TcSIOCQ>Bt2f*7bF,5]]X^^a,7gP?TmM1TeR\B8Ns*f>P31(83b-\s?kbLd*<>!&\26!nkTFYq(Nh!,J6"YqDQ6=BITdPf07.LVT"3U:b$a&FtD:8rtL:T$DD4*H`kd@kCGFLm&^+;Pr1t)Bu1lXkXm5kRTo-bh0Rile]e<+uj?981#0*5:Xj6B87VqK/u_%+c"mu-k.E'#8<4`Rk;m\*h]8k;,XHMlQP(olg(K4C4$(?.FLWW\8$.Q6PNX^XuKF:FC,G"4TNA!8rcgrKqhcm,^@E1JF(#UUr]96lq5^&lp>AF5MKWWfR;G;pkp$_F@=15GM*8GAse@*pip>lph(N=D3"d'@a/Ce,%./_hY#*!OiB6D4S"sSHKQlcB4Y-QQ#h\=FHM/1PqobaQ7oc0Fd#XcRa6hgL30AP@6/XmHjj\1EVh(AT*ct=8oArTVsMnan@@/>09+[&>]]&KN];`-*q%llS.*=#nN:7A0!jc.lT'sS44e".6g33^Bm(!EA20`r*J7,8/1r6t)C%GJQUqdB@"IScb!qo"f@kUnXD&R&"K2nm_UOH.,p0Q(Tu;,SUC1hc08_:"lG'r=.@$?AXE$p_;/_RW1<9#MM6c4p%\XJ3TnV4F_J(*$fU!'%oKKh=cB2=Kip8)bQ_'Db0%jL7g>T_`$W.jG[R_&GIFkJ22>r]L(.u+W_h0#F\SpXPTWO7;NiXVKH>jn8F,?UF>l&MSq]b))_\!9Pi0',PULBj(ilL;A=\8JLkl??aX`$2aSnf/W_*sboDj8H2&*ERJP+?B!'q&"_^J331(Zb]$Ep3@Koao9Eb_\83Y:16I%*N(8>@C;BB'jY^*V?okf2p#^-*Y9^VE"4DB%Y".5MZRCtJPL'*N/(;O1[_nIE[iZ37"Hb*Ff,.;g,a70*'&Ld(R\F>XRQr&(2i"Srbrugbl>^M':M@S?.*nt44tu'Mni4!c.Ni>\MPp=dDVO#L9[[4:"ImVX;($7QJg79_alfmka;NYg>;7)ZS."'qE&`D@_3;7@_X4">\<0*CD4Q_k1%jp%;I3j?2dNk(@O2l/)6gB&NUG1HJ>jB>!p`:)79_4'VO=;Tmu'Z$6+$A/g1+o><(-3o,KC`BZR``LPs\MDI23e:Lrb3ck/G^0=YA`uE8lHsPN$XO+aU=:.\;.!FN.9)i^C;63f!]XML=,Cbu;)$%>PE3Z@[a,=J\WS,m?Vd+/paM%<')P7fbopK`_Hg=ib!*KKfHh57cK2WP*+F,p-JR:>&;QI1%Jo&N`i?[6a#uJEFG3)trp@>#YY;(`o.219.Rb;40r7;QFs+Yd;,_R$\MD'.MUl[]ELnHl\pNi7;N+f=a[RW%30%"h,0.[\Dj.1P'0`X$R?%fhYT!D?a4Tbn>A2amLU%OZkk+9Y1/ds?21nHVKRqq@"*"Q1"ud._ZMHd+4!")\6QEI!!E96n\X1-0&"nAcYH\V"I35%(dS>q@iI5e>WV)$-7RRfE3J%&OF7_YsarPt;i(PP'.EWX8+Io]I#ng#`;SjKL+[oU(.76g;B+J3Xo8QJ7$"MS(.87\FZVlip/2g/)EA!"h$LRBKt6fA-L,X.su]rV=dV5n@`j!a96@.",?*$s83!/1s3mX$+P\&C/O:d(I3gjkrJ!bJrK>'V_M,@<"],K;"C4c2N]=RNf-SH#&Ch4N7ng#H>O+T;oIUXC2%+OE?+Q*,b^\n$,.JP*@.=!+qm\Tb:c@L]SCp,(LpF.Z0r=&t4ZRJUh2E!Z0p"9*t)hWRu'lH'JsO18Gd@leXp`ra?Ss7\VUb6@X-to@c1s8='l?Gh.F%m%PF![[/!q647I#Yi;>+")LA'.=S)s[`^t'5a@*p)P[.]U8)t5RO]kU5fa:T:KN`W]%$mu5R2.:]>,OO[LgQB,+(gtuQt#VOZ)IDX059:V!\gLuPbZ6,V'KG'nN6-&4#R1`(4r\I@o-RL&:1fH(L]n9(a4hJ9*p+LUcAR7fPL?a\62)7^lIdrJD1]Nm'Z)u8SK=KpV]9Ml8Pt6`=g?D!?#cc-U.Gg+Y[Sc9>P^W.'j=qVV5mGOO[`,/sp"hoUejsp\TQ\D)KS*KeUcE6'+Gg9lUlI'H++ST"IY12ikaB56^r.X!U"!tR[kcae7fot\'-&$4K=XT]q4/n<\_?nJB4Rs%1/(Ysg_h&W6)C:%O&L::M*tK?\:Jm28F0eQ9K-+g)MAim-s4a+mD'!a(GfeM.&UEnfOCYqU;e2bj&FYtP3JeGZDFrj-=7M8";o\5"Oh0f"pqWG)Dj!!K0Ig+;aoHa(NklX_O(5H6J+"QUc-'$]tlid4-?bd1J5/;F#<`-9_6/%qDV[5$_@O8MjeetDfn_9[;Tn7'UF!V8e88N*;"MJ7k4USUL*O]XP:d_F8+PK1q=?-)>JOp"a:KSZYCtp9Ke1(3"ZYMLI%CUFY_*f[GG^ipk"H.8=*B4`V)$Ym4K9.sM:uH(6Ku+92'FhbYTdhR!C6?XbF_a-eNj(.1rAC(!6eH`r&gA@qn2AckF;6)$ML]YkZ$?B@5f%Or0T[.b@bYV!HkN!>Sdn/^$`\<2+o\E+e&Dm6pF1?uQ^@WU@n&F+O6nH))RR<<(0AfI(&ZV2:+i.$:%:D_c[KM)@A^W3c@ojtMbU+^RXV#bJ;a*dC6$EU"-FTf6hZl?%1S5rfs97`a/Q3hWPUa=64X$Hh3`/71'G&]S24&*'Nj.HTIGk6-R1R!I7.52^>ENAiAi2u;?$OAR*ZS5R-NA;Om.;?&G.?&4be0=0e+%I.m@EfHRdhMo:Gb/C]hL6-'GQj&lBHq499ZIKs*F4`Wd\*ZjF^eXOAA<.0=LZV7hO"'n/o&u&K#jdQ^I(:lQebIF_da2"OT(653s3bA>?$?/;.l&#Llcj8VaF0,?GNGo.=RI8=iGP$!H_X"RT";^Q^8>_HfF9nb8\>(g3E;7[qgR0t]dm,2X43#c?d=Dd#WU1?SIbYEO.6gHbQ0>VpeTK[(%-ujZMq`J($)U(P@d\^\$475s=enK2&i5cs,RN4qGG+i8`d%2_qWC"73T?Q,&8Ps%SF@5T>kj`T?(:HL5E=KRq:@>Qc$[X)0_dE;i8#fPG5W_Q0>kW.J^.V,!"HNMXQ>2O_\<C,jX,JT;%qSB+^Xf\cK0GeajW32qWdAW*_CeR>s=`P2rcNNC1OJJa]qYg@=G$@H(\\/h\qi*Y&;94Uot3f9O;-aeu7P`9"K#/(EF30*'.@TLm(:4;'7h.,0=qt/apnI'L&6hT!)aj8>V]Tem6[I=$!u44]C3k)>J6bhU>C?+UbgEcuTupDh1=B,Z'ZWt,2Rs/c/'-(@9>Ra,[=d8ul<=M?=,F(:\?KeR,?4$CNL,l9#1#Y?fX>Fp"Wd7N&_bG)(o$hbE4.5hSWc%NKQIIpQZJ^n$MJ0EIibK`\jj6U]R[r&`cUk'lX7HJRAr8r93sMMuNHG\,=q^KOJn\;Y@BCLsdB;]8BM>%/d45]T]eQDGtS<%KZAMT=!mH3E\;AV>BY<7sYHDH;X.@Ciqo#g,RbH'jj8E^YTc"r-!dO]g\lKh48`WE6H>HI'*l'Xi*cm4/cS]64t0*CTQ6VPHaZ@TL\r/&ZWMIl3gm9%R[.h_N`:QA?Zo7870R;Cql*@)0Q]#YH$iTGP1Jn@6k;YlJr6RKnB.#?,K)i5C@O0JRk`U*-+t8Pmh8m@DTOR,6k[k3XQ3\/I9<>jC=s4M."gCBc2fN6TB6pJ^9'TB.Hs1D'l[0Kn]-p*(tR]D'm0:JuJ>@H@1qU/[/-H/J\?dEPnEaZ!:I<`R:BO08Pm8G@*f@@\b=M]5;9a7boi4ujR[?W*^#IqJ+:Xh@Yr!X;'%VXQ/;B"j_:b+Muk2bZ(\j&oB#nif=W1X4@GKIVVn%n8MrG?0"?^qL`2@5=VYUm@XWP5A$$,J7$66?ZQ'oiZ>0,S*7lDshDM2]\s8gq10knJ5ZblsKhgJ1Et$$nW2cVRZhEdVa)WEoE@(0i"Yu#q]\qc4@oS9BOf-:LKsQeVO,Ehn66l^s`]"YT/n`aoH3j.Rhda\PjUMsPUNMg@.Udd8E(q@;A#>H",nK:@kp@QDBK9+$-(0\J:mrK%*bj>!c-6ru<60#nWNkfnPDJ6.10?f4m\;4I>bo*,*=H"6g1`!^n1?h''iL5cUplWtS)^TKj]-n:8bCfB\$E(bq.<@/r1Dk#/3s8Wq5,0bXmK3.=uZ9S\mJR%*i@#RPsQfF-%-_<*^2;B::,5Qjpm844Hq#_MWIBqcq<7!s2RZG//mm?"ioL`bK=ciLpJ1pdfY]-_6pK2b5IV$e_+1(b]4ikETo0<&$/&5>DM+V5c6askDY_@^`0:Q0kd$/I"R6M\c='b0+gO[,oM.4BPo[;O?TZjJPh*t[&+kn&)n&sG/nd*+;hSVg:mYA6MO?"I5h1_X8/g3-(2Ln']13!eZ:SKOd?IQ:#le6s'h^DWY_pJH-F?&#;;le!(5FiBq+4bH>/[N=%=;Pb@TT\sf>J+)0/l?S--4t=Ba5JqGC-*FIKH3P2UNm9b22]=q-/%B:ceH;^VU"b2Y0?AV_EB$>kk!as@cY6J4k[Fk9EpUHlWqCj:()Z8MG3@_-Eqs2JFch_V`];t2*W9WC%@h`Z#J`rl!j\4!sJ5LDeP4si1T0/JXie1i#(eo?E(C+B>H)#LL%/uJF)BFp*14?56j53!jgISW'Vp;!q>o5:jd=1*S%J[X*#E2rOKV]2//gD]=@?"R+6?=(VEIs6@($F^MKcbjkd==M?8Kgqr'.QR:4-)S:3t+tO<8?ZWi'macLI_^^N1*FocS5$p#Ef&iLXQSOkQ:lqC8*eb:R'j#SQYpNI_A#+fH$r4T70cP+Yr9TN#1+E(Gk+@)LWBMj85m(Z75g!">3)2Tfcd#lf-`m)C*W=LD9#=tU='.uM=>AJG.d#rhIPNd]hYrd0V2WktL`;L=.&7JlG!F8iX$eE'e(Em!)_7Q.Vl[mX35&461\WcaI9.%sJuX^@]54OJQ$$nre%XVYer*sJ$^B#Y%>/=B9G0^k6(nuSBHpQpQu/B&]Li``Yn;[=\LEp^lL'pKb:nkh.&U=W^1R8kekjrMWh=92US>b4lChpc!>*)IUO-`B=Bq&.NU<*ZiSGjJ:=]G&+S0k%g5i/ZccbdD_>-E>H95*1%%&$A.J.X[^^\Tpd"e;kmQ,6;BJcoDiXt4uk<0-NC_.:2(uNj5Ni!'`*>QJkKr^uJii^q9r;(u'WcLl_q4G[VtMb#"Qe;+9-W6)^Ot4;+iC)ad%*;-plnRcauJ9Y3=l#F*o/Q@]aRO?5T`-LeO=&/,;4*hd->'O2a!9^2nN240)!K>=QJq>g?6H&.55@M7!`5X3Le9?XOo0Cj_D6rY]`uE.*?Vs"5V`sK,m>lDRCg)%coib)N\K4?L'^8"5d[NPHHJ$.6D'q&`:f!7a]3P6J11e2DqdD@7S-c,JruFnVPso3OJS7N(-Xr.^coi&&)Z"YGSk^VJsfHt6@Y/u=Sa^0(U%BQi+W1C8G7i5[cBY5pQJ<++5*cs@']PuFH9Fq1`?H]O_JJ9Wa]A[V[-`4%VO#SflC/IKGl\q#RFdH@LdKpUa3DD2pA/nE93q/)o3mr(%,;f4V5L4-5/V5kHY?AZ1dC3.@7`)\eh_:W5&P43I8T%"*&mFARst9@f-NhSjE\J&9I3X-.>s/Gkb[.#V9N8jPD,3PPY$J!c^?+QjB5nE_"YL)Rt$!(jhpZ\0L^PIGt%f2+[i>V68qU`*p48PYpO3.PZRkYL1fI6c4*cjTW1#jOct$tVHK+S&QX,q+ADe:b)::$45WO.=lp,PeVf<&@$5F7eKK1_i.2BH85[_TAUp>[b;"fa/*0:n#TIn7c9dKgESW=oj%oe'$S$"TsN>W;amr8jhfdSS)l-R0.2Y`Z-,[:mt*`g3YNl5pGD0q9Hr_30%YAReQ@tZgjr\`'rgeEU1=q"T?PS!?iFW7dL#!*d+dp8BdE6_9)>T*CI_@UnqJtd^I`"24#,oDI^-mASha74L1HElU[QE="l6NPrm6t[$+4+VsHEdZP(I1\N<;%=_Xg%!QtT5;F-IQE#G(;9B8;BR%1N+_'*`i$r;,SM$frL]H`WkJ?K+rk*G46bAS;g'0+P_-R`IB#l=pN]!@,O"tp%gaG"n*]0"CB=k!X`+A-3h;j2@W%eo#OuebGb`7OJ3sicfb)s%<\@;l$Y>Rg%)+32:qu.](ot(k]_bVi/lrG3=RjHW**>/qgRF0p4&_6^Ure2cgL=QB@6^NGPjRQ^]#.`So=#jp9Sdgli3$rqS.ftr][OXs%i7`J%OkA5Q1Pphu3*En,IlEjm@P`J)1"3`T6bsJ=+=ecNp!EH@lo]Q&8:cHi(%XU0YO$1];tE5u@2`$!6uP=oH`t^LY"?;%]qZ'm#31gu($ZAn-qjKe._?^+oY,Nj&,-i!2eJY_P(Hl9kG9:QXfh0l_pHQjA^KICu'tjPNaX0sc]0rZMK09%G:AgT:`]BHQaEO>!&PAJ^,H`qdJP`P_Ulr&RRl"p,Bf4"'Pp[PMZk.#:B]MM)FSTgW*96E1=`fni'FjPE<%%fX8WG$&10(g5auBoc,KXEjk#GH5XdFq3%7;/8U"TQ9n_o3qRsaYhp[DCnh=T\DBPV*mdp:ESP3s"oeLXT*[F=5)E.Wf3gq9(u=P[m^fD_.>k%jf93>Xc,5uc9-2I(78hkZrU4LV,f)TOXi:_1LtR,gHql/7@#(WHd?l4T)J2+5i#Q<`g@R#ir)f7#,1Ogiea8f^4"F(R,3Ligi*Hg]&jeV1MCmM$rYQo%87\H9$a:ZHS+D>VEU;TaD'j$:'K3JH;bdRL2H:PTL:u%n%fA8PC99R+L]T4(8i*,GTCDgI@(SM2b/5<)D,NbN__CK!ED6H1PJDmK@;_ql4X8,oNV8Y+XBJZB=RX*ALL6?>G7fD=L$oUB5$E>TMW?g4]Yu:".Q9G_[X3dA.^&74OF2bMCu@j#(kDoJ?7'nnZVeH+UYADjMf&2#B]!bq0:ip2F]o[!t2jB&#Cde=M@6WYYu6KLOBJdR-Q5GV_'Q#l.n19YNBqa0r@o[rt^O3V]4MT!JI[!JSp"S*T4Olm,1Ok[QIZP6YrAqLWX$4mCg`p-.r,"[a^o)IEn)CW7l*T6#GR-[EB'@Id'+c)g?!i_uU5P\-j2uC3'7EK[K*bpJU*"3?^5]X(HhL\i=*/UWFYVmTX#hCGfO4M_Pm!`^T1LWZh-+Vh19+oiho\'1,jb!\@]h7IO.*dTOHfRDjnEJQp?9,a%cBm)]F'I[(<=;OeF'K!G8d-I\1JAs.7.!T[V[8N7oR5hZTB'c;2:J8HQ2.D,qKI#]ruH(/BJO=ER5Ca1O1$5gG_\^-F='!O)lt1On$cd=^@uO(s(6b1ruo2*k^WA?pU0`:5O2a:dm*Y!*-8FK3H^j%`R?an4S)fq)9b5iMdbHWAVe=oH+i)*MT-6^*ce-&QpmNJ, +# ===>END WOOF<=== diff --git a/wscript b/wscript index c76724650..2a25e1f83 100644 --- a/wscript +++ b/wscript @@ -2,12 +2,13 @@ import sys import shlex import shutil +import types +import optparse +import os.path import Params import Object import pproc as subprocess -import optparse -import os.path Params.g_autoconfig = 1 @@ -36,7 +37,7 @@ def set_options(opt): opt.add_option('-d', '--debug-level', action='callback', - type=str, dest='debug_level', default='debug', + type="string", dest='debug_level', default='debug', help=('Specify the debug level, does nothing if CFLAGS is set' ' in the environment. [Allowed Values: debug, optimized].' ' WARNING: this option only has effect ' @@ -135,14 +136,33 @@ def configure(conf): conf.sub_config('src') +def create_ns3_program(bld, name, dependencies=('simulator',)): + program = bld.create_obj('cpp', 'program') + program.name = name + program.target = program.name + program.uselib_local = 'ns3' + return program + + def build(bld): + print "Entering directory `%s/build'" % Params.g_build.m_curdirnode.abspath() + Params.g_cwd_launch = Params.g_build.m_curdirnode.abspath() + + bld.create_ns3_program = types.MethodType(create_ns3_program, bld) + variant_name = bld.env_of_name('default')['NS3_ACTIVE_VARIANT'] variant_env = bld.env_of_name(variant_name) bld.m_allenvs['default'] = variant_env # switch to the active variant if Params.g_options.shell: run_shell() - return + raise SystemExit(0) + + if Params.g_options.doxygen: + doxygen() + raise SystemExit(0) + + check_shell() if Params.g_options.doxygen: doxygen() @@ -152,6 +172,11 @@ def build(bld): bld.add_subdirs('src') bld.add_subdirs('samples utils examples') + ## Create a single ns3 library containing all modules + lib = bld.create_obj('cpp', 'shlib') + lib.name = 'ns3' + lib.target = 'ns3' + lib.add_objects = list(bld.env_of_name('default')['NS3_MODULES']) def shutdown(): #import UnitTest @@ -193,34 +218,32 @@ def _find_program(program_name, env): raise ValueError("program '%s' not found; available programs are: %r" % (program_name, found_programs)) -def _run_argv(argv): +def _run_argv(argv, os_env=None): env = Params.g_build.env_of_name('default') if sys.platform == 'linux2': pathvar = 'LD_LIBRARY_PATH' - pathsep = ':' elif sys.platform == 'darwin': pathvar = 'DYLD_LIBRARY_PATH' - pathsep = ':' elif sys.platform == 'win32': pathvar = 'PATH' - pathsep = ';' elif sys.platform == 'cygwin': pathvar = 'PATH' - pathsep = ':' else: Params.warning(("Don't know how to configure " "dynamic library path for the platform '%s'") % (sys.platform,)) pathvar = None - pathsep = None - os_env = dict(os.environ) + proc_env = dict(os.environ) + if os_env is not None: + proc_env.update(os_env) + if pathvar is not None: - if pathvar in os_env: - os_env[pathvar] = pathsep.join([os_env[pathvar]] + list(env['NS3_MODULE_PATH'])) + if pathvar in proc_env: + proc_env[pathvar] = os.pathsep.join(list(env['NS3_MODULE_PATH']) + [proc_env[pathvar]]) else: - os_env[pathvar] = pathsep.join(list(env['NS3_MODULE_PATH'])) + proc_env[pathvar] = os.pathsep.join(list(env['NS3_MODULE_PATH'])) - retval = subprocess.Popen(argv, env=os_env).wait() + retval = subprocess.Popen(argv, env=proc_env).wait() if retval: Params.fatal("Command %s exited with code %i" % (argv, retval)) @@ -274,13 +297,33 @@ def run_program(program_string, command_template=None): return retval +def check_shell(): + if 'NS3_MODULE_PATH' not in os.environ: + return + env = Params.g_build.env_of_name('default') + correct_modpath = os.pathsep.join(env['NS3_MODULE_PATH']) + found_modpath = os.environ['NS3_MODULE_PATH'] + if found_modpath != correct_modpath: + msg = ("Detected shell (waf --shell) with incorrect configuration\n" + "=========================================================\n" + "Possible reasons for this problem:\n" + " 1. You switched to another ns-3 tree from inside this shell\n" + " 2. You switched ns-3 debug level (waf configure --debug)\n" + " 3. You modified the list of built ns-3 modules\n" + "You should correct this situation before running any program. Possible solutions:\n" + " 1. Exit this shell, and start a new one\n" + " 2. Run a new nested shell") + Params.fatal(msg) + def run_shell(): if sys.platform == 'win32': shell = os.environ.get("COMSPEC", "cmd.exe") else: shell = os.environ.get("SHELL", "/bin/sh") - _run_argv([shell]) + + env = Params.g_build.env_of_name('default') + _run_argv([shell], {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])}) def doxygen():