diff --git a/SConstruct b/SConstruct new file mode 100644 index 000000000..9c9a2567c --- /dev/null +++ b/SConstruct @@ -0,0 +1,521 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +import os.path +import build + +version_file = open ('VERSION', 'r') +version = version_file.readline () +version_file.close () +version = version.strip () + +ns3 = build.Ns3() +ns3.build_dir = 'build-dir' +ns3.version = version +ns3.name = 'ns3' +ns3.distname = 'ns' +ns3.doxygen_config = os.path.join('doc', 'doxygen.conf') +ns3.add_extra_dist(os.path.join('doc', 'main.txt')) +ns3.add_extra_dist ('doc/architecture.pdf') +ns3.add_extra_dist ('doc/contributing.txt') +ns3.add_extra_dist ('doc/build.txt') +ns3.add_extra_dist ('doc/codingstd.txt') +ns3.add_extra_dist ('doc/mercurial.txt') +ns3.add_extra_dist ('README') +ns3.add_extra_dist ('RELEASE_NOTES') +ns3.add_extra_dist ('AUTHORS') +ns3.add_extra_dist ('VERSION') + +ns3.add_extra_dist('doc/build-waf.txt') +ns3.add_extra_dist('ns3/_placeholder_') +for wscript in [ + "src/core/wscript", + "src/node/wscript", + "src/devices/p2p/wscript", + "src/common/wscript", + "src/applications/wscript", + "src/simulator/wscript", + "src/internet-node/wscript", + "src/wscript", + "utils/wscript", + "samples/wscript", + "examples/wscript", + "wscript", + ]: + ns3.add_extra_dist(wscript) +ns3.add_extra_dist('waf') +ns3.add_extra_dist('waf.bat') + +# +# The Core module +# +core = build.Ns3Module('core', 'src/core') +ns3.add(core) +core.add_sources([ + 'callback-test.cc', + 'debug.cc', + 'assert.cc', + 'ptr.cc', + 'object.cc', + 'test.cc', + 'random-variable.cc', + 'rng-stream.cc', + 'uid-manager.cc', + 'default-value.cc', + 'command-line.cc', + 'type-name.cc', + 'component-manager.cc', + ]) +env = Environment() +if env['PLATFORM'] == 'posix' or env['PLATFORM'] == 'darwin' or env['PLATFORM'] == 'cygwin': + core.add_external_dep('pthread') + core.add_sources([ + 'unix-system-wall-clock-ms.cc', + ]) +elif env['PLATFORM'] == 'win32': + core.add_sources([ + 'win32-system-wall-clock-ms.cc', + ]) +core.add_headers ([ + 'uid-manager.h', + 'singleton.h', +]) +core.add_inst_headers([ + 'system-wall-clock-ms.h', + 'empty.h', + 'callback.h', + 'ptr.h', + 'object.h', + 'debug.h', + 'assert.h', + 'fatal-error.h', + 'test.h', + 'random-variable.h', + 'rng-stream.h', + 'default-value.h', + 'command-line.h', + 'type-name.h', + 'component-manager.h', + ]) + +def config_core (env, config): + retval = [] + # XXX This check is primitive but it should be + # good enough for now. + if config.CheckCHeader ('stdlib.h') == 1: + retval.append ('#define HAVE_STDLIB_H 1') + retval.append ('#define HAVE_GETENV 1') + else: + retval.append ('#undef HAVE_STDLIB_H') + retval.append ('#undef HAVE_GETENV') + return retval +core.add_config (config_core) + +# +# The Simulator module +# +simu = build.Ns3Module('simulator', 'src/simulator') +ns3.add(simu) +simu.add_dep('core') +simu.add_external_dep('m') +simu.add_sources([ + 'high-precision.cc', + 'time.cc', + 'event-id.cc', + 'scheduler.cc', + 'scheduler-factory.cc', + 'scheduler-list.cc', + 'scheduler-heap.cc', + 'scheduler-map.cc', + 'event-impl.cc', + 'simulator.cc', + ]) +simu.add_headers([ + 'scheduler-heap.h', + 'scheduler-map.h', + 'scheduler-list.h' + ]) +simu.add_inst_headers([ + 'high-precision.h', + 'nstime.h', + 'event-id.h', + 'event-impl.h', + 'simulator.h', + 'scheduler.h', + 'scheduler-factory.h', + 'simulation-singleton.h', + ]) +high_precision_as_double = ARGUMENTS.get('high-precision-as-double', 'n') +if high_precision_as_double == 'y': + simu.add_inst_header ('high-precision-double.h') + simu.add_source ('high-precision-double.cc') +else: + simu.add_inst_headers ([ + 'high-precision-128.h', + 'cairo-wideint-private.h' + ]) + simu.add_sources ([ + 'high-precision-128.cc', + 'cairo-wideint.c', + ]) + +def config_simulator (env, config): + retval = [] + high_precision_as_double = ARGUMENTS.get('high-precision-as-double', 'n') + if high_precision_as_double == 'y': + retval.append ('#define USE_HIGH_PRECISION_DOUBLE 1') + else: + retval.append ('#undef USE_HIGH_PRECISION_DOUBLE') + if config.CheckCHeader ('stdint.h') == 1: + retval.append ('#define HAVE_STDINT_H 1') + elif config.CheckCHeader ('inttypes.h') == 1: + retval.append ('#define HAVE_INTTYPES_H 1') + elif config.CheckCHeader ('sys/inttypes.h') == 1: + retval.append ('#define HAVE_SYS_INT_TYPES_H 1') + return retval +simu.add_config (config_simulator) + +# +# The Common module +# +common = build.Ns3Module('common', 'src/common') +common.add_deps(['core', 'simulator']) +ns3.add(common) +common.add_sources([ + 'buffer.cc', + 'chunk.cc', + 'header.cc', + 'trailer.cc', + 'packet-printer.cc', + 'packet-metadata.cc', + 'packet.cc', + 'tags.cc', + 'pcap-writer.cc', + 'variable-tracer-test.cc', + 'trace-context.cc', + 'trace-resolver.cc', + 'callback-trace-source.cc', + 'empty-trace-resolver.cc', + 'composite-trace-resolver.cc', + 'trace-root.cc', + 'data-rate.cc', + ]) +common.add_headers ([ + ]) +common.add_inst_headers([ + 'buffer.h', + 'chunk.h', + 'header.h', + 'trailer.h', + 'tags.h', + 'packet.h', + 'packet-printer.h', + 'packet-metadata.h', + 'uv-trace-source.h', + 'sv-trace-source.h', + 'fv-trace-source.h', + 'pcap-writer.h', + 'callback-trace-source.h', + 'trace-context.h', + 'trace-resolver.h', + 'empty-trace-resolver.h', + 'composite-trace-resolver.h', + 'array-trace-resolver.h', + 'trace-root.h', + 'terminal-trace-resolver.h', + 'data-rate.h', + ]) + +# +# The Node module +# +node = build.Ns3Module ('node', 'src/node') +ns3.add (node) +node.add_deps (['core', 'common', 'simulator']) +node.add_sources ([ + 'node.cc', + 'ipv4-address.cc', + 'net-device.cc', + 'mac-address.cc', + 'llc-snap-header.cc', + 'ipv4-route.cc', + 'queue.cc', + 'drop-tail-queue.cc', + 'channel.cc', + 'node-list.cc', + 'socket.cc', + 'socket-factory.cc', + 'udp.cc', + 'ipv4.cc', + 'application.cc', + ]) +node.add_inst_headers ([ + 'node.h', + 'ipv4-address.h', + 'net-device.h', + 'mac-address.h', + 'ipv4-route.h', + 'queue.h', + 'drop-tail-queue.h', + 'llc-snap-header.h', + 'channel.h', + 'node-list.h', + 'socket.h', + 'socket-factory.h', + 'udp.h', + 'ipv4.h', + 'application.h', + ]) + +# +# The Applications module +# +applications = build.Ns3Module ('applications', 'src/applications') +ns3.add (applications) +applications.add_deps (['node']) +applications.add_sources ([ + 'onoff-application.cc', +]) +applications.add_inst_headers ([ + 'onoff-application.h', +]) + +# +# The Internet Node module +# +inode = build.Ns3Module ('internet-node', 'src/internet-node') +ns3.add (inode) +inode.add_deps (['node', 'routing']) +inode.add_sources ([ + 'internet-node.cc', + 'l3-demux.cc', + 'l3-protocol.cc', + 'ipv4-l4-demux.cc', + 'ipv4-l4-protocol.cc', + 'ipv4-header.cc', + 'udp-header.cc', + 'ipv4-checksum.cc', + 'ipv4-interface.cc', + 'ipv4-l3-protocol.cc', + 'ipv4-end-point.cc', + 'udp-l4-protocol.cc', + 'arp-header.cc', + 'arp-cache.cc', + 'arp-ipv4-interface.cc', + 'arp-l3-protocol.cc', + 'ipv4-loopback-interface.cc', + 'header-utils.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', +]) +inode.add_headers ([ + 'ipv4-checksum.h', + 'arp-header.h', + 'arp-cache.h', + 'arp-l3-protocol.h', + 'ipv4-loopback-interface.h', + 'l3-demux.h', + 'header-utils.h', + 'arp-ipv4-interface.h', + 'udp-socket.h', + 'udp-l4-protocol.h', + 'arp-private.h', + 'ipv4-impl.h', + 'ipv4-private.h', + 'ipv4-l3-protocol.h', + 'l3-protocol.h', + 'ipv4-l4-protocol.h', + 'ipv4-l4-demux.h', + 'ipv4-end-point-demux.h', + 'ipv4-end-point.h', + 'ipv4-header.h', + 'ipv4-interface.h', + 'udp-header.h', + 'sgi-hashmap.h', + 'udp-impl.h', +]) +inode.add_inst_headers ([ + 'internet-node.h', + 'ascii-trace.h', + 'pcap-trace.h', + 'ipv4-header.h', + 'udp-header.h', +]) + +# +# The Point-to-point module +# +p2p = build.Ns3Module ('p2p', 'src/devices/p2p') +ns3.add (p2p) +p2p.add_deps (['node']) +p2p.add_sources ([ + 'p2p-net-device.cc', + 'p2p-channel.cc', + 'p2p-topology.cc', + ]) +p2p.add_inst_headers ([ + 'p2p-net-device.h', + 'p2p-channel.h', + 'p2p-topology.h', + ]) + +# +# The Routing module +# +routing = build.Ns3Module('routing', 'src/routing') +routing.add_deps(['core', 'node']) +ns3.add(routing) +routing.add_sources([ + 'routing-environment.cc', + 'static-router.cc', + 'static-route-manager.cc', + 'static-route-manager-impl.cc', + 'candidate-queue.cc', + ]) +routing.add_headers ([ + 'candidate-queue.h', + 'static-route-manager-impl.h', + ]) +routing.add_inst_headers([ + 'routing-environment.h', + 'static-router.h', + 'static-route-manager.h', + ]) + +# utils +run_tests = build.Ns3Module('run-tests', 'utils') +ns3.add(run_tests) +run_tests.set_executable() +run_tests.add_deps(['core', 'simulator', 'common', 'routing']) +run_tests.add_source('run-tests.cc') + +bench_object = build.Ns3Module('bench-object', 'utils') +ns3.add(bench_object) +bench_object.set_executable() +bench_object.add_deps(['core']) +bench_object.add_source('bench-object.cc') + +bench_packets = build.Ns3Module('bench-packets', 'utils') +ns3.add(bench_packets) +bench_packets.set_executable() +bench_packets.add_deps (['core', 'common']) +bench_packets.add_source('bench-packets.cc') + +bench_simu = build.Ns3Module('bench-simulator', 'utils') +ns3.add(bench_simu) +bench_simu.set_executable() +bench_simu.add_dep('simulator') +bench_simu.add_source('bench-simulator.cc') + +replay_simu = build.Ns3Module('replay-simulation', 'utils') +ns3.add(replay_simu) +replay_simu.set_executable() +replay_simu.add_dep('simulator') +replay_simu.add_source('replay-simulation.cc') + + +# samples +sample_debug = build.Ns3Module('sample-debug', 'samples') +sample_debug.set_executable() +ns3.add(sample_debug) +sample_debug.add_dep('core') +sample_debug.add_source('main-debug.cc') +sample_debug.add_source('main-debug-other.cc') + +sample_packet_printer = build.Ns3Module('sample-packet-printer', 'samples') +sample_packet_printer.set_executable() +ns3.add(sample_packet_printer) +sample_packet_printer.add_deps (['common', 'internet-node']) +sample_packet_printer.add_source('main-packet-printer.cc') + +sample_callback = build.Ns3Module('sample-callback', 'samples') +sample_callback.set_executable() +ns3.add(sample_callback) +sample_callback.add_dep('core') +sample_callback.add_source('main-callback.cc') + +sample_ptr = build.Ns3Module('sample-ptr', 'samples') +sample_ptr.set_executable() +ns3.add(sample_ptr) +sample_ptr.add_dep('core') +sample_ptr.add_source('main-ptr.cc') + +sample_trace = build.Ns3Module('sample-trace', 'samples') +#ns3.add(sample_trace) +sample_trace.add_dep('common') +sample_trace.set_executable() +sample_trace.add_source('main-trace.cc') + +sample_query_interface = build.Ns3Module('sample-query-interface', 'samples') +ns3.add(sample_query_interface) +sample_query_interface.add_dep('common') +sample_query_interface.set_executable() +sample_query_interface.add_source('main-query-interface.cc') + +sample_simu = build.Ns3Module('sample-simulator', 'samples') +ns3.add(sample_simu) +sample_simu.set_executable() +sample_simu.add_dep('simulator') +sample_simu.add_source('main-simulator.cc') + +sample_packet = build.Ns3Module('sample-packet', 'samples') +ns3.add(sample_packet) +sample_packet.set_executable() +sample_packet.add_dep('common') +sample_packet.add_source('main-packet.cc') + +sample_test = build.Ns3Module('sample-test', 'samples') +sample_test.set_executable() +ns3.add(sample_test) +sample_test.add_dep('core') +sample_test.add_source('main-test.cc') + +sample_simple = build.Ns3Module('sample-simple', 'samples') +sample_simple.set_executable() +ns3.add(sample_simple) +sample_simple.add_deps(['core', 'simulator', 'node', 'internet-node', 'routing']) +sample_simple.add_source('main-simple.cc') + +sample_sp2p = build.Ns3Module('sample-simple-p2p', 'samples') +sample_sp2p.set_executable() +#n3.add(sample_sp2p) +sample_sp2p.add_deps(['core', 'simulator', 'node', 'internet-node', 'p2p']) +sample_sp2p.add_source('main-simple-p2p.cc') + +sample_default_value = build.Ns3Module('sample-default-value', 'samples') +sample_default_value.set_executable() +ns3.add(sample_default_value) +sample_default_value.add_deps(['core', 'simulator', 'node', 'p2p']) +sample_default_value.add_source('main-default-value.cc') + +sample_object = build.Ns3Module('sample-object', 'samples') +sample_object.set_executable() +ns3.add(sample_object) +sample_object.add_deps(['core']) +sample_object.add_source('main-object.cc') + +sample_component_manager = build.Ns3Module('sample-component-manager', 'samples') +sample_component_manager.set_executable() +ns3.add(sample_component_manager) +sample_component_manager.add_deps(['core']) +sample_component_manager.add_source('main-component-manager.cc') + +# examples +example_simple_p2p = build.Ns3Module('simple-p2p', 'examples') +example_simple_p2p.set_executable() +ns3.add(example_simple_p2p) +example_simple_p2p.add_deps(['core', 'simulator', 'node', 'p2p', 'internet-node', 'applications', 'routing']) +example_simple_p2p.add_source('simple-p2p.cc') + +example_static_routing = build.Ns3Module('simple-static-routing', 'examples') +example_static_routing.set_executable() +ns3.add(example_static_routing) +example_static_routing.add_deps(['core', 'simulator', 'node', 'p2p', 'internet-node', 'applications', 'routing']) +example_static_routing.add_source('simple-static-routing.cc') + +ns3.generate_dependencies() diff --git a/examples/simple-global-routing.cc b/examples/simple-global-routing.cc new file mode 100644 index 000000000..2a005589a --- /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/mac-address.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv4.h" +#include "ns3/socket.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 Bind command tells the queue factory which class to + // instantiate, when the queue factory is invoked in the topology code + Bind ("Queue", "DropTailQueue"); + + Bind ("OnOffApplicationPacketSize", "210"); + Bind ("OnOffApplicationDataRate", "448kb/s"); + + //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); + + // 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, + Ipv4Address ("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, + Ipv4Address ("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 (); +} diff --git a/examples/simple-p2p.cc b/examples/simple-p2p.cc new file mode 100644 index 000000000..9ad235b5e --- /dev/null +++ b/examples/simple-p2p.cc @@ -0,0 +1,191 @@ +/* -*- 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 p2p 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-p2p.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/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/p2p-channel.h" +#include "ns3/p2p-net-device.h" +#include "ns3/mac-address.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv4.h" +#include "ns3/socket.h" +#include "ns3/ipv4-route.h" +#include "ns3/p2p-topology.h" +#include "ns3/onoff-application.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"); +#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 Bind command tells the queue factory which class to + // instantiate, when the queue factory is invoked in the topology code + Bind ("Queue", "DropTailQueue"); + + Bind ("OnOffApplicationPacketSize", "210"); + Bind ("OnOffApplicationDataRate", "448kb/s"); + + //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); + + // 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")); + + // Finally, we add static routes. These three steps (Channel and + // NetDevice creation, IP Address assignment, and routing) are + // separated because there may be a need to postpone IP Address + // assignment (emulation) or modify to use dynamic routing + PointToPointTopology::AddIpv4Routes(n0, n2, channel0); + PointToPointTopology::AddIpv4Routes(n1, n2, channel1); + PointToPointTopology::AddIpv4Routes(n2, n3, channel2); + + + // Create the OnOff application to send UDP datagrams of size + // 210 bytes at a rate of 448 Kb/s + Ptr ooff = Create ( + n0, + Ipv4Address("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, + Ipv4Address("10.1.2.1"), + 80, + "Udp", + ConstantVariable(1), + ConstantVariable(0)); + // Start the application + ooff->Start(Seconds(1.1)); + ooff->Stop (Seconds(10.0)); + + // Here, finish off packet routing configuration + // This will likely set by some global StaticRouting object in the future + Ptr ipv4; + ipv4 = n0->QueryInterface (Ipv4::iid); + ipv4->SetDefaultRoute (Ipv4Address ("10.1.1.2"), 1); + ipv4 = n3->QueryInterface (Ipv4::iid); + ipv4->SetDefaultRoute (Ipv4Address ("10.1.3.1"), 1); + + // Configure tracing of all enqueue, dequeue, and NetDevice receive events + // Trace output will be sent to the simple-p2p.tr file + AsciiTrace asciitrace ("simple-p2p.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-p2p.pcap"); + pcaptrace.TraceAllIp (); + + Simulator::Run (); + + Simulator::Destroy (); +} diff --git a/examples/wscript b/examples/wscript index e1b960ddc..0f15f23df 100644 --- a/examples/wscript +++ b/examples/wscript @@ -9,6 +9,10 @@ def build(bld): 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 = create_ns_prog('simple-global-routing', 'simple-global-routing.cc', + deps=['point-to-point', 'internet-node', 'global-routing']) + 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']) diff --git a/src/devices/p2p/p2p-channel.cc b/src/devices/p2p/p2p-channel.cc new file mode 100644 index 000000000..cbc3eb805 --- /dev/null +++ b/src/devices/p2p/p2p-channel.cc @@ -0,0 +1,186 @@ +/* -*- 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 "p2p-channel.h" +#include "p2p-net-device.h" +#include "ns3/packet.h" +#include "ns3/simulator.h" +#include "ns3/debug.h" + +NS_DEBUG_COMPONENT_DEFINE ("PointToPointChannel"); + +namespace ns3 { + +// +// By default, you get a channel with the name "PointToPoint Channel" that +// has an "infitely" fast transmission speed and zero delay. +// +PointToPointChannel::PointToPointChannel() +: + Channel ("PointToPoint Channel"), + m_bps (DataRate(0xffffffff)), + m_delay (Seconds(0)), + m_nDevices(0) +{ + NS_DEBUG("PointToPointChannel::PointToPointChannel ()"); +} + +PointToPointChannel::PointToPointChannel( + const DataRate& bps, + const Time& delay) +: + Channel ("PointToPoint Channel"), + m_bps (bps), + m_delay (delay), + m_nDevices(0) +{ + NS_DEBUG("PointToPointChannel::PointToPointChannel (" << Channel::GetName() + << ", " << bps.GetBitRate() << ", " << delay << ")"); +} + +PointToPointChannel::PointToPointChannel( + const std::string& name, + const DataRate& bps, + const Time& delay) +: + Channel (name), + m_bps (bps), + m_delay (delay), + m_nDevices(0) +{ + NS_DEBUG("PointToPointChannel::PointToPointChannel (" << name << ", " << + bps.GetBitRate() << ", " << delay << ")"); +} + + void +PointToPointChannel::Attach(Ptr device) +{ + NS_DEBUG("PointToPointChannel::Attach (" << device << ")"); + NS_ASSERT(m_nDevices < N_DEVICES && "Only two devices permitted"); + NS_ASSERT(device != 0); + + m_link[m_nDevices].m_src = device; + ++m_nDevices; +// +// If we have both devices connected to the channel, then finish introducing +// the two halves and set the links to IDLE. +// + if (m_nDevices == N_DEVICES) + { + m_link[0].m_dst = m_link[1].m_src; + m_link[1].m_dst = m_link[0].m_src; + m_link[0].m_state = IDLE; + m_link[1].m_state = IDLE; + } +} + + bool +PointToPointChannel::TransmitStart(Packet& p, Ptr src) +{ + NS_DEBUG ("PointToPointChannel::TransmitStart (" << &p << ", " << src << + ")"); + NS_DEBUG ("PointToPointChannel::TransmitStart (): UID is " << + p.GetUid () << ")"); + + NS_ASSERT(m_link[0].m_state != INITIALIZING); + NS_ASSERT(m_link[1].m_state != INITIALIZING); + + uint32_t wire = src == m_link[0].m_src ? 0 : 1; + + if (m_link[wire].m_state == TRANSMITTING) + { + NS_DEBUG("PointToPointChannel::TransmitStart (): **** ERROR ****"); + NS_DEBUG("PointToPointChannel::TransmitStart (): state TRANSMITTING"); + return false; + } + + NS_DEBUG("PointToPointChannel::TransmitStart (): switch to TRANSMITTING"); + m_link[wire].m_state = TRANSMITTING; + return true; +} + + bool +PointToPointChannel::TransmitEnd(Packet& p, Ptr src) +{ + NS_DEBUG("PointToPointChannel::TransmitEnd (" << &p << ", " << src << ")"); + NS_DEBUG ("PointToPointChannel::TransmitEnd (): UID is " << + p.GetUid () << ")"); + + NS_ASSERT(m_link[0].m_state != INITIALIZING); + NS_ASSERT(m_link[1].m_state != INITIALIZING); + + uint32_t wire = src == m_link[0].m_src ? 0 : 1; + + NS_ASSERT(m_link[wire].m_state == TRANSMITTING); + + m_link[wire].m_state = PROPAGATING; +// +// The sender is going to free the packet as soon as it has been transmitted. +// We need to copy it to get a reference so it won't e deleted. +// + Packet packet = p; + NS_DEBUG ("PointToPointChannel::TransmitEnd (): Schedule event in " << + m_delay.GetSeconds () << "sec"); + Simulator::Schedule (m_delay, + &PointToPointChannel::PropagationCompleteEvent, + this, packet, src); + return true; +} + + void +PointToPointChannel::PropagationCompleteEvent( + Packet p, + Ptr src) +{ + NS_DEBUG("PointToPointChannel::PropagationCompleteEvent (" << &p << ", " << + src << ")"); + NS_DEBUG ("PointToPointChannel::PropagationCompleteEvent (): UID is " << + p.GetUid () << ")"); + + uint32_t wire = src == m_link[0].m_src ? 0 : 1; + NS_ASSERT(m_link[wire].m_state == PROPAGATING); + m_link[wire].m_state = IDLE; + + NS_DEBUG ("PointToPointChannel::PropagationCompleteEvent (): Receive"); + m_link[wire].m_dst->Receive (p); +} + + uint32_t +PointToPointChannel::GetNDevices (void) const +{ + return m_nDevices; +} + + Ptr +PointToPointChannel::GetDevice (uint32_t i) const +{ + NS_ASSERT(i < 2); + return m_link[i].m_src; +} + + DataRate +PointToPointChannel::GetDataRate (void) +{ + return m_bps; +} + + Time +PointToPointChannel::GetDelay (void) +{ + return m_delay; +} + +} // namespace ns3 diff --git a/src/devices/p2p/p2p-channel.h b/src/devices/p2p/p2p-channel.h new file mode 100644 index 000000000..b534df915 --- /dev/null +++ b/src/devices/p2p/p2p-channel.h @@ -0,0 +1,128 @@ +/* -*- 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 POINT_TO_POINT_CHANNEL_H +#define POINT_TO_POINT_CHANNEL_H + +#include +#include "ns3/channel.h" +#include "ns3/ptr.h" +#include "ns3/packet.h" +#include "ns3/nstime.h" +#include "ns3/data-rate.h" + +namespace ns3 { + +class PointToPointNetDevice; + +/** + * \brief Simple Point To Point Channel. + * + * This class represents a very simple point to point channel. Think full + * duplex RS-232 or RS-422 with null modem and no handshaking. There is no + * multi-drop capability on this channel -- there can be a maximum of two + * point-to-point net devices connected. Once we start talking about multi- + * drop, or CSMA, or some other sharing mechanism, things begin getting + * complicated quickly. Rather than invent some ad-hoc mechanism, we just + * Keep It Simple everywhere. + * + * When the channel is instaniated, the constructor takes parameters for + * a single speed, in bits per second, and a speed-of-light delay time as a + * Time object. Both directions use the same speed and delay time. + * + * There are two "wires" in the channel. The first device connected gets the + * [0] wire to transmit on. The second device gets the [1] wire. There is a + * state (IDLE, TRANSMITTING) associated with each wire. + */ +class PointToPointChannel : public Channel { +public: +// +// This is really kidding myself, since just setting N_DEVICES to 3 isn't +// going to come close to magically creating a multi-drop link, but I can't +// bring myself to just type 2 in the code (even though I type 0 and 1 :-). +// + static const int N_DEVICES = 2; + /** + * \brief Create a PointToPointChannel + * + * By default, you get a channel with the name "PointToPoint Channel" that + * has an "infitely" fast transmission speed and zero delay. + */ + PointToPointChannel (); + + /** + * \brief Create a PointToPointChannel + * + * \param bps The bitrate of the channel + * \param delay Transmission delay through the channel + */ + PointToPointChannel (const DataRate& bps, const Time& delay); + + /** + * \brief Create a PointToPointChannel + * + * \param name the name of the channel for identification purposes + * \param bps The bitrate of the channel + * \param delay Transmission delay through the channel + */ + PointToPointChannel (const std::string& name, + const DataRate& bps, const Time& delay); + + /** + * \brief Attach a given netdevice to this channel + * \param device pointer to the netdevice to attach to the channel + */ + void Attach (Ptr device); + bool TransmitStart (Packet& p, Ptr src); + bool TransmitEnd (Packet &p, Ptr src); + void PropagationCompleteEvent(Packet p, Ptr src); + + + virtual uint32_t GetNDevices (void) const; + virtual Ptr GetDevice (uint32_t i) const; + + virtual DataRate GetDataRate (void); + virtual Time GetDelay (void); + +private: + DataRate m_bps; + Time m_delay; + + int32_t m_nDevices; + + enum WireState + { + INITIALIZING, + IDLE, + TRANSMITTING, + PROPAGATING + }; + + class Link + { + public: + Link() : m_state (INITIALIZING), m_src (0), m_dst (0) {} + WireState m_state; + Ptr m_src; + Ptr m_dst; + }; + + Link m_link[N_DEVICES]; +}; + +} // namespace ns3 + +#endif /* POINT_TO_POINT_CHANNEL_H */ diff --git a/src/devices/p2p/p2p-net-device.cc b/src/devices/p2p/p2p-net-device.cc new file mode 100644 index 000000000..9542d951b --- /dev/null +++ b/src/devices/p2p/p2p-net-device.cc @@ -0,0 +1,354 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,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 +#include +#include "ns3/debug.h" +#include "ns3/queue.h" +#include "ns3/simulator.h" +#include "ns3/composite-trace-resolver.h" +#include "p2p-net-device.h" +#include "p2p-channel.h" + +NS_DEBUG_COMPONENT_DEFINE ("PointToPointNetDevice"); + +namespace ns3 { + +PointToPointNetDevice::PointToPointNetDevice (Ptr node) +: + NetDevice(node, MacAddress ("00:00:00:00:00:00")), + m_txMachineState (READY), + m_bps (DataRate (0xffffffff)), + m_tInterframeGap (Seconds(0)), + m_channel (0), + m_queue (0), + m_rxTrace () +{ + NS_DEBUG ("PointToPointNetDevice::PointToPointNetDevice (" << node << ")"); + + // BUGBUG FIXME + // + // You _must_ support broadcast to get any sort of packet from the ARP layer. + EnableBroadcast (MacAddress ("ff:ff:ff:ff:ff:ff")); + EnableMulticast(); + EnablePointToPoint(); +} + +PointToPointNetDevice::~PointToPointNetDevice() +{ + NS_DEBUG ("PointToPointNetDevice::~PointToPointNetDevice ()"); + m_queue = 0; +} + +// +// Copy constructor for PointToPointNetDevice. +// +// We use the underlying NetDevice copy constructor to get the base class +// copied. These just remain as is (e.g. you get the same name, the same +// MAC address). If you need to fix them up, YOU, the copier need to do +// that. +// +// The things we need to be careful of are the channel, the queue and the +// trace callback. If the channel pointer is non-zero, we copy the pointer +// and add a reference. If the queue is non-zero, we copy it using the queue +// assignment operator. We don't mess with the trace -- we just reset it. +// We're assuming that the tracing will be set up after the topology creation +// phase and this won't actually matter. +// +PointToPointNetDevice::PointToPointNetDevice (const PointToPointNetDevice& nd) +: + NetDevice(nd), + m_txMachineState(READY), + m_bps (nd.m_bps), + m_tInterframeGap (nd.m_tInterframeGap), + m_channel(nd.m_channel), + m_queue(0), + m_rxTrace () +{ + NS_DEBUG ("PointToPointNetDevice::PointToPointNetDevice (" << &nd << ")"); + + if (nd.m_queue) + { + m_queue = nd.m_queue; + } + +} + +void PointToPointNetDevice::DoDispose() +{ + m_channel = 0; + NetDevice::DoDispose (); +} + +// +// Assignment operator for PointToPointNetDevice. +// +// 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 +// invoked (where the real work is done -- see above). All we have to do +// here is to return the newly constructed net device. +// + PointToPointNetDevice& +PointToPointNetDevice::operator= (const PointToPointNetDevice nd) +{ + NS_DEBUG ("PointToPointNetDevice::operator= (" << &nd << ")"); + return *this; +} + + void +PointToPointNetDevice::SetDataRate(DataRate bps) +{ + m_bps = bps; +} + + void +PointToPointNetDevice::SetInterframeGap(Time t) +{ + m_tInterframeGap = t; +} + + bool +PointToPointNetDevice::SendTo (Packet& p, const MacAddress& dest) +{ + NS_DEBUG ("PointToPointNetDevice::SendTo (" << &p << ", " << &dest << ")"); + NS_DEBUG ("PointToPointNetDevice::SendTo (): UID is " << p.GetUid () << ")"); + + NS_ASSERT (IsLinkUp ()); + +#ifdef NOTYET + struct NetDevicePacketDestAddress tag; + tag.address = address; + p.AddTag (tag); +#endif + +// +// This class simulates a point to point device. In the case of a serial +// link, this means that we're simulating something like a UART. This is +// not a requirement for a point-to-point link, but it's a typical model for +// the device. +// +// Generally, a real device will have a list of pending packets to transmit. +// An on-device CPU frees the main CPU(s) of the details of what is happening +// in the device and feeds the USART. The main CPU basically just sees the +// list of packets -- it puts packets into the list, and the device frees the +// packets when they are transmitted. +// +// In the case of our virtual device here, the queue pointed to by m_queue +// corresponds to this list. The main CPU adds packets to the list by +// calling this method and when the device completes a send, the packets are +// freed in an "interrupt" service routine. +// +// We're going to do the same thing here. So first of all, the incoming packet +// goes onto our queue if possible. If the queue can't handle it, there's +// nothing to be done. +// + if (m_queue->Enqueue(p) == false ) + { + return false; + } +// +// If there's a transmission in progress, the "interrupt" will keep the +// transmission process going. If the device is idle, we need to start a +// transmission. +// +// In the real world, the USART runs until it finishes sending bits, and then +// pulls on the device's transmit complete interrupt wire. At the same time, +// the electrons from the last wiggle of the wire are busy propagating down +// the wire. In the case of a long speed-of-light delay in the wire, we could +// conceivably start transmitting the next packet before the end of the +// previously sent data has even reached the end of the wire. This situation +// is usually avoided (like the plague) and an "interframe gap" is introduced. +// This is usually the round-trip delay on the channel plus some hard-to- +// quantify receiver turn-around time (the time required for the receiver +// to process the last frame and prepare for reception of the next). +// +// So, if the transmit machine is ready, we need to schedule a transmit +// complete event (at which time we tell the channel we're no longer sending +// bits). A separate transmit ready event (at which time the transmitter +// becomes ready to start sending bits again is scheduled there). Finally, +// we tell the channel (via TransmitStart ()) that we've started wiggling the +// wire and bits are coming out. +// +// If the transmit machine is not ready, we just leave and the transmit ready +// event we know is coming will kick-start the transmit process. +// + if (m_txMachineState == READY) + { + return TransmitStart (p); + } + return true; +} + + bool +PointToPointNetDevice::TransmitStart (Packet &p) +{ + NS_DEBUG ("PointToPointNetDevice::TransmitStart (" << &p << ")"); + NS_DEBUG ( + "PointToPointNetDevice::TransmitStart (): UID is " << p.GetUid () << ")"); +// +// This function is called to start the process of transmitting a packet. +// We need to tell the channel that we've started wiggling the wire and +// schedule an event that will be executed when it's time to tell the +// channel that we're done wiggling the wire. +// + NS_ASSERT_MSG(m_txMachineState == READY, "Must be READY to transmit"); + m_txMachineState = BUSY; + Time tEvent = Seconds (m_bps.CalculateTxTime(p.GetSize())); + + NS_DEBUG ("PointToPointNetDevice::TransmitStart (): " << + "Schedule TransmitCompleteEvent in " << + tEvent.GetSeconds () << "sec"); + + Simulator::Schedule (tEvent, + &PointToPointNetDevice::TransmitCompleteEvent, + this); + return m_channel->TransmitStart (p, this); +} + + void +PointToPointNetDevice::TransmitCompleteEvent (void) +{ + NS_DEBUG ("PointToPointNetDevice::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 +// schedule an event that will be executed when it's time to re-enable +// the transmitter after the interframe gap. +// + NS_ASSERT_MSG(m_txMachineState == BUSY, "Must be BUSY if transmitting"); + m_txMachineState = GAP; + Packet p; + bool found; + found = m_queue->Dequeue (p); + NS_ASSERT_MSG(found, "Packet must be on queue if transmitted"); + NS_DEBUG ("PointToPointNetDevice::TransmitCompleteEvent (): Pkt UID is " << + p.GetUid () << ")"); + m_channel->TransmitEnd (p, this); + + NS_DEBUG ( + "PointToPointNetDevice::TransmitCompleteEvent (): " << + "Schedule TransmitReadyEvent in " + << m_tInterframeGap.GetSeconds () << "sec"); + + Simulator::Schedule (m_tInterframeGap, + &PointToPointNetDevice::TransmitReadyEvent, + this); +} + + void +PointToPointNetDevice::TransmitReadyEvent (void) +{ + NS_DEBUG ("PointToPointNetDevice::TransmitReadyEvent ()"); +// +// This function is called to enable the transmitter after the interframe +// gap has passed. If there are pending transmissions, we use this opportunity +// to start the next transmit. +// + NS_ASSERT_MSG(m_txMachineState == GAP, "Must be in interframe gap"); + m_txMachineState = READY; + + if (m_queue->IsEmpty()) + { + return; + } + else + { + Packet p; + bool found; + found = m_queue->Peek (p); + NS_ASSERT_MSG(found, "IsEmpty false but no Packet on queue?"); + TransmitStart (p); + } +} + +TraceResolver * +PointToPointNetDevice::DoCreateTraceResolver (TraceContext const &context) +{ + CompositeTraceResolver *resolver = new CompositeTraceResolver (context); + resolver->Add ("queue", + MakeCallback (&Queue::CreateTraceResolver, PeekPointer (m_queue)), + PointToPointNetDevice::QUEUE); + resolver->Add ("rx", + m_rxTrace, + PointToPointNetDevice::RX); + return resolver; +} + +bool +PointToPointNetDevice::Attach (Ptr ch) +{ + NS_DEBUG ("PointToPointNetDevice::Attach (" << &ch << ")"); + + m_channel = ch; + + m_channel->Attach(this); + m_bps = m_channel->GetDataRate (); + m_tInterframeGap = m_channel->GetDelay (); + + /* + * For now, this device is up whenever a channel is attached to it. + * In fact, it should become up only when the second device + * is attached to the channel. So, there should be a way for + * a PointToPointChannel to notify both of its attached devices + * that the channel is 'complete', hence that the devices are + * up, hence that they can call NotifyLinkUp. + */ + NotifyLinkUp (); + return true; +} + +void +PointToPointNetDevice::AddQueue (Ptr q) +{ + NS_DEBUG ("PointToPointNetDevice::AddQueue (" << q << ")"); + + m_queue = q; +} + +void +PointToPointNetDevice::Receive (Packet& p) +{ + // ignore return value for now. + NS_DEBUG ("PointToPointNetDevice::Receive (" << &p << ")"); + + m_rxTrace (p); + ForwardUp (p); +} + +Ptr +PointToPointNetDevice::GetQueue(void) const +{ + return m_queue; +} + +Ptr +PointToPointNetDevice::DoGetChannel(void) const +{ + return m_channel; +} + +bool +PointToPointNetDevice::DoNeedsArp (void) const +{ + return false; +} + +} // namespace ns3 diff --git a/src/devices/p2p/p2p-net-device.h b/src/devices/p2p/p2p-net-device.h new file mode 100644 index 000000000..e1eb8a666 --- /dev/null +++ b/src/devices/p2p/p2p-net-device.h @@ -0,0 +1,313 @@ +/* -*- 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; + * + * 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: Craig Dowell + */ + +#ifndef POINT_TO_POINT_NET_DEVICE_H +#define POINT_TO_POINT_NET_DEVICE_H + +#include +#include "ns3/mac-address.h" +#include "ns3/node.h" +#include "ns3/net-device.h" +#include "ns3/callback.h" +#include "ns3/packet.h" +#include "ns3/callback-trace-source.h" +#include "ns3/nstime.h" +#include "ns3/data-rate.h" +#include "ns3/ptr.h" + +namespace ns3 { + +class Queue; +class PointToPointChannel; + +/** + * \class PointToPointNetDevice + * \brief A Device for a Point to Point Network Link. + * + * Ns-3 takes a four-layer view of a protocol stack. This is the same model + * that TCP uses. In this view, layers 5-7 of the OSI reference model are + * grouped together into an application layer; layer four (transport / TCP) is + * broken out; layer three (network / IP) is broken out; and layers 1-2 are + * grouped together. We call this grouping of layers one and two a NetDevice + * and represent it as a class in the system. + * + * The NetDevice class is specialized according to the needs of the specific + * kind of network link. In this case, the link is a PointToPoint link. The + * PointToPoint link is a family of classes that includes this class, the + * PointToPointNetDevice, a PointToPointChannel class that represents the + * actual medium across which bits are sent, a PointToPointIpv4Interface class + * that provides the hook to tie a general purpose node to this specific + * link, and finally, a PointToPointTopology object that is responsible for + * putting all of the pieces together. + * + * This is the PointToPointNetDevice class that represents, essentially, the + * PC card that is used to connect to the PointToPoint network. + */ +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 + * + * This is the constructor for the PointToPointNetDevice. 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 deleded. + * + * @see PointToPointTopology::AddPointToPointLink () + * @param node the Node to which this device is connected. + */ + PointToPointNetDevice (Ptr node); + /** + * Copy Construct a PointToPointNetDevice + * + * This is the copy constructor for the PointToPointNetDevice. This is + * primarily used in topology creation. + * + * @see PointToPointTopology::AddPointToPointLink () + * @param nd the object to be copied + */ + PointToPointNetDevice (const PointToPointNetDevice& nd); + /** + * Destroy a PointToPointNetDevice + * + * This is the destructor for the PointToPointNetDevice. + */ + virtual ~PointToPointNetDevice(); + /** + * Assignment Operator for a PointToPointNetDevice + * + * This is the assignment operator for the PointToPointNetDevice. This is + * to allow + * + * @param nd the object to be copied + */ + PointToPointNetDevice& operator= (PointToPointNetDevice nd); + /** + * 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 + * to which the device is attached. It can be overridden using this method. + * + * @see Attach () + * @param bps the data rate at which this object operates + */ + void SetDataRate(DataRate bps); + /** + * Set the inteframe gap used to separate packets. The interframe gap + * defines the minimum space required between packets sent by this device. + * It is usually set in the Attach () method based on the speed of light + * delay of the channel to which the device is attached. It can be + * overridden using this method if desired. + * + * @see Attach () + * @param t the interframe gap time + */ + void SetInterframeGap(Time t); + /** + * Attach the device to a channel. + * + * The PointToPointTopology object creates a PointToPointChannel and two + * PointtoPointNetDevices. In order to introduce these components to each + * other, the topology object calls Attach () on each PointToPointNetDevice. + * Inside this method, the Net Device calls out to the PointToPointChannel + * to introduce itself. + * + * @see PointToPointTopology::AddPointToPointLink () + * @see SetDataRate () + * @see SetInterframeGap () + * @param ch a pointer to the channel to which this object is being attached. + */ + bool Attach(Ptr ch); + /** + * Attach a queue to the PointToPointNetDevice. + * + * The PointToPointNetDevice "owns" a queue. This queue is created by the + * PointToPointTopology object and implements a queueing method such as + * DropTail or RED. The PointToPointNetDevice assumes ownership of this + * queue and must delete it when the device is destroyed. + * + * @see PointToPointTopology::AddPointToPointLink () + * @see Queue + * @see DropTailQueue + * @param queue a pointer to the queue for which object is assuming + * ownership. + */ + void AddQueue(Ptr queue); + /** + * Receive a packet from a connected PointToPointChannel. + * + * The PointToPointNetDevice 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 PointToPointChannel + * @param p a reference to the received packet + */ + void Receive (Packet& p); +protected: + virtual void DoDispose (void); + /** + * Get a copy of the attached Queue. + * + * This method is provided for any derived class that may need to get + * direct access to the underlying queue. + * + * @see PointToPointTopology + * @returns a pointer to the queue. + */ + Ptr GetQueue(void) const; + /** + * Get a copy of the attached Channel + * + * This method is provided for any derived class that may need to get + * direct access to the connected channel + * + * @see PointToPointChannel + * @returns a pointer to the channel + */ + virtual Ptr DoGetChannel(void) const; +private: + /** + * Send a Packet Down the Wire. + * + * The SendTo method is defined as the standard way that the level three + * protocol uses to tell a NetDevice to send a packet. SendTo is declared + * as abstract in the NetDevice class and we declare it here. + * + * @see NetDevice + * @param p a reference to the packet to send + * @param dest a reference to the MacAddress of the destination device + * @returns true if success, false on failure + */ + virtual bool SendTo (Packet& p, const MacAddress& dest); + /** + * Start Sending a Packet Down the Wire. + * + * The TransmitStart method is the method that is used internally in the + * PointToPointNetDevice 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. An event is scheduled for the time at which + * the bits have been completely transmitted. + * + * @see PointToPointChannel::TransmitStart () + * @see TransmitCompleteEvent () + * @param p a reference to the packet to send + * @returns true if success, false on failure + */ + bool TransmitStart (Packet &p); + /** + * Stop Sending a Packet Down the Wire and Begin the Interframe Gap. + * + * The TransmitCompleteEvent method is used internally to finish the process + * of sending a packet out on the channel. During execution of this method + * the TransmitEnd method is called on the channel to let it know that the + * physical device this class represents has virually finished sending + * signals. The channel uses this event to begin its speed of light delay + * timer after which it notifies the Net Device at the other end of the + * link that the bits have arrived. During this method, the net device + * also schedules the TransmitReadyEvent at which time the transmitter + * becomes ready to send the next packet. + * + * @see PointToPointChannel::TransmitEnd () + * @see TransmitReadyEvent () + * @returns true if success, false on failure + */ + void TransmitCompleteEvent (void); + /** + * Cause the Transmitter to Become Ready to Send Another Packet. + * + * The TransmitReadyEvent method is used internally to re-enable the + * transmit machine of the net device. It is scheduled after a suitable + * interframe gap after the completion of the previous transmission. + * The queue is checked at this time, and if there is a packet waiting on + * the queue, the transmission process is begun. + * + * @see TransmitStart () + */ + void TransmitReadyEvent (void); + /** + * Create a Trace Resolver for events in the net device. + * + * @see class TraceResolver + */ + virtual TraceResolver *DoCreateTraceResolver (TraceContext const &context); + virtual bool DoNeedsArp (void) const; + /** + * Enumeration of the states of the transmit machine of the net device. + */ + enum TxMachineState + { + READY, /**< The transmitter is ready to begin transmission of a packet */ + BUSY, /**< The transmitter is busy transmitting a packet */ + GAP /**< The transmitter is in the interframe gap time */ + }; + /** + * The state of the Net Device transmit state machine. + * @see TxMachineState + */ + TxMachineState m_txMachineState; + /** + * The data rate that the Net Device uses to simulate packet transmission + * timing. + * @see class DataRate + */ + DataRate m_bps; + /** + * The interframe gap that the Net Device uses to throttle packet + * transmission + * @see class Time + */ + Time m_tInterframeGap; + /** + * The PointToPointChannel to which this PointToPointNetDevice has been + * attached. + * @see class PointToPointChannel + */ + Ptr m_channel; + /** + * The Queue which this PointToPointNetDevice uses as a packet source. + * Management of this Queue has been delegated to the PointToPointNetDevice + * and it has the responsibility for deletion. + * @see class Queue + * @see class DropTailQueue + */ + Ptr m_queue; + /** + * The trace source for the packet reception events that the device can + * fire. + * + * @see class CallBackTraceSource + * @see class TraceResolver + */ + CallbackTraceSource m_rxTrace; +}; + +}; // namespace ns3 + +#endif // POINT_TO_POINT_NET_DEVICE_H + diff --git a/src/devices/p2p/p2p-topology.cc b/src/devices/p2p/p2p-topology.cc new file mode 100644 index 000000000..0b0ee003b --- /dev/null +++ b/src/devices/p2p/p2p-topology.cc @@ -0,0 +1,172 @@ +/* -*- 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 +// + +// +// Topology helper for ns3. +// George F. Riley, Georgia Tech, Spring 2007 + +#include +#include "ns3/assert.h" +#include "ns3/debug.h" +#include "ns3/fatal-error.h" +#include "ns3/nstime.h" +#include "ns3/internet-node.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv4.h" +#include "ns3/queue.h" + +#include "p2p-channel.h" +#include "p2p-net-device.h" +#include "p2p-topology.h" + +namespace ns3 { + +Ptr +PointToPointTopology::AddPointToPointLink( + Ptr n1, + Ptr n2, + const DataRate& bps, + const Time& delay) +{ + Ptr channel = Create (bps, delay); + + Ptr net1 = Create (n1); + + Ptr q = Queue::CreateDefault (); + net1->AddQueue(q); + net1->Attach (channel); + + Ptr net2 = Create (n2); + + q = Queue::CreateDefault (); + net2->AddQueue(q); + net2->Attach (channel); + + return channel; +} + +void +PointToPointTopology::AddIpv4Addresses( + Ptr chan, + Ptr n1, const Ipv4Address& addr1, + Ptr n2, const Ipv4Address& addr2) +{ + + // Duplex link is assumed to be subnetted as a /30 + // May run this unnumbered in the future? + Ipv4Mask netmask("255.255.255.252"); + NS_ASSERT (netmask.IsMatch(addr1,addr2)); + + // The PointToPoint channel is used to find the relevant NetDevices + NS_ASSERT (chan->GetNDevices () == 2); + Ptr nd1 = chan->GetDevice (0); + Ptr nd2 = chan->GetDevice (1); + // Make sure that nd1 belongs to n1 and nd2 to n2 + if ( (nd1->GetNode ()->GetId () == n2->GetId () ) && + (nd2->GetNode ()->GetId () == n1->GetId () ) ) + { + std::swap(nd1, nd2); + } + NS_ASSERT (nd1->GetNode ()->GetId () == n1->GetId ()); + NS_ASSERT (nd2->GetNode ()->GetId () == n2->GetId ()); + + Ptr ip1 = n1->QueryInterface (Ipv4::iid); + uint32_t index1 = ip1->AddInterface (nd1); + + ip1->SetAddress (index1, addr1); + ip1->SetNetworkMask (index1, netmask); + ip1->SetUp (index1); + + Ptr ip2 = n2->QueryInterface (Ipv4::iid); + uint32_t index2 = ip2->AddInterface (nd2); + + ip2->SetAddress (index2, addr2); + ip2->SetNetworkMask (index2, netmask); + ip2->SetUp (index2); + +} + +void +PointToPointTopology::AddIpv4Routes ( + Ptr n1, Ptr n2, Ptr chan) +{ + // The PointToPoint channel is used to find the relevant NetDevices + NS_ASSERT (chan->GetNDevices () == 2); + Ptr nd1 = chan->GetDevice (0); + Ptr nd2 = chan->GetDevice (1); + + // Assert that n1 is the Node owning one of the two NetDevices + // and make sure that nd1 corresponds to it + if (nd1->GetNode ()->GetId () == n1->GetId ()) + { + ; // Do nothing + } + else if (nd2->GetNode ()->GetId () == n1->GetId ()) + { + std::swap(nd1, nd2); + } + else + { + NS_FATAL_ERROR("P2PTopo: Node does not contain an interface on Channel"); + } + + // Assert that n2 is the Node owning one of the two NetDevices + // and make sure that nd2 corresponds to it + if (nd2->GetNode ()->GetId () != n2->GetId ()) + { + NS_FATAL_ERROR("P2PTopo: Node does not contain an interface on Channel"); + } + + // Assert that both are Ipv4 nodes + Ptr ip1 = nd1->GetNode ()->QueryInterface (Ipv4::iid); + Ptr ip2 = nd2->GetNode ()->QueryInterface (Ipv4::iid); + NS_ASSERT(ip1 != 0 && ip2 != 0); + + // Get interface indexes for both nodes corresponding to the right channel + uint32_t index1 = 0; + bool found = false; + for (uint32_t i = 0; i < ip1->GetNInterfaces (); i++) + { + if (ip1 ->GetNetDevice (i) == nd1) + { + index1 = i; + found = true; + } + } + NS_ASSERT(found); + + uint32_t index2 = 0; + found = false; + for (uint32_t i = 0; i < ip2->GetNInterfaces (); i++) + { + if (ip2 ->GetNetDevice (i) == nd2) + { + index2 = i; + found = true; + } + } + NS_ASSERT(found); + + ip1->AddHostRouteTo (ip2-> GetAddress (index2), index1); + ip2->AddHostRouteTo (ip1-> GetAddress (index1), index2); +} + +} // namespace ns3 + diff --git a/src/devices/p2p/p2p-topology.h b/src/devices/p2p/p2p-topology.h new file mode 100644 index 000000000..c1f037380 --- /dev/null +++ b/src/devices/p2p/p2p-topology.h @@ -0,0 +1,87 @@ +/* -*- 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 +// +// Topology helper for ns3. +// George F. Riley, Georgia Tech, Spring 2007 +#ifndef __POINT_TO_POINT_TOPOLOGY_H__ +#define __POINT_TO_POINT_TOPOLOGY_H__ + +#include "ns3/ptr.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 PointToPointChannel; +class Node; +class IPAddr; +class DataRate; +class Queue; + +/** + * \brief A helper class to create Topologies based on the + * ns3::PointToPointNetDevice and ns3::PointToPointChannel objects. + */ +class PointToPointTopology { +public: + /** + * \param n1 Node + * \param n2 Node + * \param dataRate Maximum transmission link rate + * \param delay one-way propagation delay + * \return Pointer to the underlying PointToPointChannel + * + * Add a full-duplex point-to-point link between two nodes + * and attach PointToPointNetDevices to the resulting + * PointToPointChannel. + */ + static Ptr AddPointToPointLink( + Ptr n1, Ptr n2, const DataRate& dataRate, const Time& delay); + + /** + * \param chan PointToPointChannel to use + * \param n1 Node + * \param addr1 Ipv4 Address for n1 + * \param n2 Node + * \param addr2 Ipv4 Address for n2 + * + * Add Ipv4Addresses to the Ipv4 interfaces associated with the + * two PointToPointNetDevices on the provided PointToPointChannel + */ + static void AddIpv4Addresses( + Ptr chan, + Ptr n1, const Ipv4Address& addr1, + Ptr n2, const Ipv4Address& addr2); + + /** + * \param channel PointToPointChannel to use + * \param n1 Node + * \param n2 Node + * + * For the given PointToPointChannel, for each Node, add an + * IPv4 host route to the IPv4 address of the peer node. + */ + static void AddIpv4Routes (Ptr n1, Ptr n2, Ptr channel); +}; + +} // namespace ns3 + +#endif + diff --git a/src/devices/p2p/wscript b/src/devices/p2p/wscript new file mode 100644 index 000000000..c4c0f7952 --- /dev/null +++ b/src/devices/p2p/wscript @@ -0,0 +1,24 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + + +def configure(conf): + conf.env.append_value('NS3_MODULES', 'ns3-p2p') + + +def build(bld): + p2p = bld.create_obj('cpp', 'shlib') + p2p.name = 'ns3-p2p' + p2p.target = p2p.name + p2p.uselib_local = ['ns3-node'] + p2p.source = [ + 'p2p-net-device.cc', + 'p2p-channel.cc', + 'p2p-topology.cc', + ] + headers = bld.create_obj('ns3header') + headers.source = [ + 'p2p-net-device.h', + 'p2p-channel.h', + 'p2p-topology.h', + ] + diff --git a/src/internet-node/internet-node.cc b/src/internet-node/internet-node.cc index 0400e241f..26acb8ce2 100644 --- a/src/internet-node/internet-node.cc +++ b/src/internet-node/internet-node.cc @@ -37,7 +37,6 @@ namespace ns3 { - InternetNode::InternetNode() { Construct (); @@ -78,7 +77,6 @@ InternetNode::Construct (void) Object::AddInterface (ipv4L4Demux); } - TraceResolver * InternetNode::DoCreateTraceResolver (TraceContext const &context) { 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/ipv4-address.cc b/src/node/ipv4-address.cc index c716930fd..374a124ee 100644 --- a/src/node/ipv4-address.cc +++ b/src/node/ipv4-address.cc @@ -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 { diff --git a/src/node/ipv4-address.h b/src/node/ipv4-address.h index cddfc176c..e33025fac 100644 --- a/src/node/ipv4-address.h +++ b/src/node/ipv4-address.h @@ -51,6 +51,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 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..03578cb96 --- /dev/null +++ b/src/routing/global-routing/global-route-manager-impl.cc @@ -0,0 +1,1408 @@ +/* -*- 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 (GlobalRouterLSA* lsa) : + m_vertexType (VertexRouter), + 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 () +{ +} + +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 (GlobalRouterLSA* lsa) +{ + m_lsa = lsa; +} + + GlobalRouterLSA* +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"); + GlobalRouterLSA* 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++) + { + GlobalRouterLSA* temp = i->second; + temp->SetStatus (GlobalRouterLSA::LSA_SPF_NOT_EXPLORED); + } +} + + void +GlobalRouteManagerLSDB::Insert (Ipv4Address addr, GlobalRouterLSA* lsa) +{ + NS_DEBUG ("GlobalRouteManagerLSDB::Insert ()"); + m_database.insert (LSDBPair_t (addr, lsa)); +} + + GlobalRouterLSA* +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; +} + +// --------------------------------------------------------------------------- +// +// 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 ()"); + + typedef std::vector < Ptr >::iterator Iterator; + for (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. +// + typedef std::vector < Ptr >::iterator Iterator; + for (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) + { + GlobalRouterLSA* lsa = new GlobalRouterLSA (); +// +// This is the call to actually fetch a Link State Advertisement from the +// router. +// + rtr->GetLSA (j, *lsa); + NS_DEBUG ("LSA " << j); + 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. +// + typedef std::vector < Ptr >::iterator Iterator; + for (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; + GlobalRouterLSA* w_lsa = 0; + uint32_t distance = 0; + + NS_DEBUG ("GlobalRouteManagerImpl::SPFNext ()"); +// +// Always true for now, since all our LSAs are RouterLSAs. +// + if (v->GetVertexType () == SPFVertex::VertexRouter) + { + if (true) + { + NS_DEBUG ("SPFNext: Examining " << v->GetVertexId () << "'s " << + v->GetLSA ()->GetNLinkRecords () << " link records"); +// +// Walk the list of link records in the link state advertisement associated +// with the "current" router (represented by vertex ). +// + for (uint32_t i = 0; i < v->GetLSA ()->GetNLinkRecords (); ++i) + { +// +// (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. +// + GlobalRouterLinkRecord *l = v->GetLSA ()->GetLinkRecord (i); + if (l->GetLinkType () == GlobalRouterLinkRecord::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 () == GlobalRouterLinkRecord::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 ()); +// +// (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 () == + GlobalRouterLSA::LSA_SPF_IN_SPFTREE) + { + NS_DEBUG ("SPFNext: Skipping-> LSA "<< + w_lsa->GetLinkStateId () << " already in SPF tree"); + continue; + } +// +// The link is to a router we haven't dealt with yet. +// +// (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. +// + distance = v->GetDistanceFromRoot () + l->GetMetric (); + + NS_DEBUG ("SPFNext: Considering w_lsa " << + w_lsa->GetLinkStateId ()); + + if (w_lsa->GetStatus () == + GlobalRouterLSA::LSA_SPF_NOT_EXPLORED) + { +// +// If we haven't yet considered the link represented by we have to create +// a new SPFVertex to represent it. +// + w = new SPFVertex (w_lsa); +// +// 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 ( + GlobalRouterLSA::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 () == + GlobalRouterLSA::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 (); + } + } + } // point-to-point + } // for loop + } + } +} + +// +// This method is derived from quagga ospf_next_hop_calculation() 16.1.1. +// +// Calculate the next hop IP address and the outgoing interface required to +// get packets from the root through (parent) to vertex (destination), +// over a given distance. +// +// For now, this is greatly simplified from the quagga code +// + int +GlobalRouteManagerImpl::SPFNexthopCalculation ( + SPFVertex* v, + SPFVertex* w, + GlobalRouterLinkRecord* l, + uint32_t distance) +{ + NS_DEBUG ("GlobalRouteManagerImpl::SPFNexthopCalculation ()"); +// +// 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. +// + GlobalRouterLinkRecord *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 ()); + } + } + 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 . +// +// BUGBUG FIXME: This seems to be a bug. Shouldn't this function look for +// any link records after pre_link and not just after the first? +// + GlobalRouterLinkRecord* +GlobalRouteManagerImpl::SPFGetNextLink ( + SPFVertex* v, + SPFVertex* w, + GlobalRouterLinkRecord* prev_link) +{ + NS_DEBUG ("GlobalRouteManagerImpl::SPFGetNextLink ()"); + + bool skip = true; + GlobalRouterLinkRecord* l; +// +// If prev_link is 0, we are really looking for the first link, not the next +// link. +// + if (prev_link == 0) + { + skip = false; + } +// +// 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); + if (l->GetLinkType () != GlobalRouterLinkRecord::PointToPoint) + { + continue; + } +// +// 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 ()) { + 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 (GlobalRouterLSA::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 (GlobalRouterLSA::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. +// + SPFIntraAddRouter (v); +// +// 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. +// + std::vector >::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; +} + +// +// 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. +// + std::vector >::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. +// + GlobalRouterLSA *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 +// + GlobalRouterLinkRecord *lr = lsa->GetLinkRecord (j); + if (lr->GetLinkType () != GlobalRouterLinkRecord::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; + } + } +} + +// 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" + +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 + GlobalRouterLinkRecord* lr0 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::PointToPoint, + "0.0.0.2", // router ID 0.0.0.2 + "10.1.1.1", // local ID + 1); // metric + + GlobalRouterLinkRecord* lr1 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::StubNetwork, + "10.1.1.1", + "255.255.255.252", + 1); + + GlobalRouterLSA* lsa0 = new GlobalRouterLSA (); + lsa0->SetLinkStateId ("0.0.0.0"); + lsa0->SetAdvertisingRouter ("0.0.0.0"); + lsa0->AddLinkRecord (lr0); + lsa0->AddLinkRecord (lr1); + + // Router 1 + GlobalRouterLinkRecord* lr2 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::PointToPoint, + "0.0.0.2", + "10.1.2.1", + 1); + + GlobalRouterLinkRecord* lr3 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::StubNetwork, + "10.1.2.1", + "255.255.255.252", + 1); + + GlobalRouterLSA* lsa1 = new GlobalRouterLSA (); + lsa1->SetLinkStateId ("0.0.0.1"); + lsa1->SetAdvertisingRouter ("0.0.0.1"); + lsa1->AddLinkRecord (lr2); + lsa1->AddLinkRecord (lr3); + + // Router 2 + GlobalRouterLinkRecord* lr4 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::PointToPoint, + "0.0.0.0", + "10.1.1.2", + 1); + + GlobalRouterLinkRecord* lr5 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::StubNetwork, + "10.1.1.2", + "255.255.255.252", + 1); + + GlobalRouterLinkRecord* lr6 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::PointToPoint, + "0.0.0.1", + "10.1.2.2", + 1); + + GlobalRouterLinkRecord* lr7 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::StubNetwork, + "10.1.2.2", + "255.255.255.252", + 1); + + GlobalRouterLinkRecord* lr8 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::PointToPoint, + "0.0.0.3", + "10.1.3.2", + 1); + + GlobalRouterLinkRecord* lr9 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::StubNetwork, + "10.1.3.2", + "255.255.255.252", + 1); + + GlobalRouterLSA* lsa2 = new GlobalRouterLSA (); + 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 + GlobalRouterLinkRecord* lr10 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::PointToPoint, + "0.0.0.2", + "10.1.2.1", + 1); + + GlobalRouterLinkRecord* lr11 = new GlobalRouterLinkRecord ( + GlobalRouterLinkRecord::StubNetwork, + "10.1.2.1", + "255.255.255.252", + 1); + + GlobalRouterLSA* lsa3 = new GlobalRouterLSA (); + 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 + + // 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..7979cb4a2 --- /dev/null +++ b/src/routing/global-routing/global-route-manager-impl.h @@ -0,0 +1,747 @@ +/* -*- 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 GlobalRouterLSA + * @param lsa The Link State Advertisement used for finding initial values. + */ + SPFVertex(GlobalRouterLSA* 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 GlobalRouterLSA + * @see GlobalRouter::DiscoverLSAs () + * @returns A pointer to the GlobalRouterLSA found by the router represented + * by this SPFVertex object. + */ + GlobalRouterLSA* 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 GlobalRouterLSA + * @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 GlobalRouterLSA. + */ + void SetLSA (GlobalRouterLSA* 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 GlobalRouterLSA + * @see GlobalRouterLinkRecord + * @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 GlobalRouterLSA + * @see GlobalRouterLinkRecord + * @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 GlobalRouterLSA + * @see GlobalRouterLinkRecord + * @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 GlobalRouterLSA + * @see GlobalRouterLinkRecord + * @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; + GlobalRouterLSA* 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 GlobalRouterLSA given as parameters are converted + * to an STL pair and are inserted into the database map. + * + * @see GlobalRouterLSA + * @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, GlobalRouterLSA* lsa); + +/** + * @brief Look up the Link State Advertisement associated with the given + * IP Address. + * @internal + * + * The database map is searched for the given IPV4 address and corresponding + * GlobalRouterLSA is returned. + * + * @see GlobalRouterLSA + * @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. + */ + GlobalRouterLSA* GetLSA (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 GlobalRouterLSA + * @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, + GlobalRouterLinkRecord* l, uint32_t distance); + void SPFVertexAddParent (SPFVertex* v); + GlobalRouterLinkRecord* SPFGetNextLink (SPFVertex* v, SPFVertex* w, + GlobalRouterLinkRecord* prev_link); + void SPFIntraAddRouter (SPFVertex* v); + uint32_t FindOutgoingInterfaceId (Ipv4Address a); +}; + +} // 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..24a84be06 --- /dev/null +++ b/src/routing/global-routing/global-router-interface.cc @@ -0,0 +1,588 @@ +/* -*- 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 { + +// --------------------------------------------------------------------------- +// +// GlobalRouterLinkRecord Implementation +// +// --------------------------------------------------------------------------- + +GlobalRouterLinkRecord::GlobalRouterLinkRecord () +: + m_linkId ("0.0.0.0"), + m_linkData ("0.0.0.0"), + m_linkType (Unknown), + m_metric (0) +{ + NS_DEBUG("GlobalRouterLinkRecord::GlobalRouterLinkRecord ()"); +} + +GlobalRouterLinkRecord::GlobalRouterLinkRecord ( + LinkType linkType, + Ipv4Address linkId, + Ipv4Address linkData, + uint32_t metric) +: + m_linkId (linkId), + m_linkData (linkData), + m_linkType (linkType), + m_metric (metric) +{ + NS_DEBUG("GlobalRouterLinkRecord::GlobalRouterLinkRecord (" << + linkType << ", " << linkId << ", " << linkData << ", " << metric << ")"); +} + +GlobalRouterLinkRecord::~GlobalRouterLinkRecord () +{ + NS_DEBUG("GlobalRouterLinkRecord::~GlobalRouterLinkRecord ()"); +} + + Ipv4Address +GlobalRouterLinkRecord::GetLinkId (void) const +{ + NS_DEBUG("GlobalRouterLinkRecord::GetLinkId ()"); + return m_linkId; +} + + void +GlobalRouterLinkRecord::SetLinkId (Ipv4Address addr) +{ + NS_DEBUG("GlobalRouterLinkRecord::SetLinkId ()"); + m_linkId = addr; +} + + Ipv4Address +GlobalRouterLinkRecord::GetLinkData (void) const +{ + NS_DEBUG("GlobalRouterLinkRecord::GetLinkData ()"); + return m_linkData; +} + + void +GlobalRouterLinkRecord::SetLinkData (Ipv4Address addr) +{ + NS_DEBUG("GlobalRouterLinkRecord::SetLinkData ()"); + m_linkData = addr; +} + + GlobalRouterLinkRecord::LinkType +GlobalRouterLinkRecord::GetLinkType (void) const +{ + NS_DEBUG("GlobalRouterLinkRecord::GetLinkType ()"); + return m_linkType; +} + + void +GlobalRouterLinkRecord::SetLinkType ( + GlobalRouterLinkRecord::LinkType linkType) +{ + NS_DEBUG("GlobalRouterLinkRecord::SetLinkType ()"); + m_linkType = linkType; +} + + uint32_t +GlobalRouterLinkRecord::GetMetric (void) const +{ + NS_DEBUG("GlobalRouterLinkRecord::GetMetric ()"); + return m_metric; +} + + void +GlobalRouterLinkRecord::SetMetric (uint32_t metric) +{ + NS_DEBUG("GlobalRouterLinkRecord::SetMetric ()"); + m_metric = metric; +} + +// --------------------------------------------------------------------------- +// +// GlobalRouterLSA Implementation +// +// --------------------------------------------------------------------------- + +GlobalRouterLSA::GlobalRouterLSA() + : + m_linkStateId("0.0.0.0"), + m_advertisingRtr("0.0.0.0"), + m_linkRecords(), + m_status(GlobalRouterLSA::LSA_SPF_NOT_EXPLORED) +{ + NS_DEBUG("GlobalRouterLSA::GlobalRouterLSA ()"); +} + +GlobalRouterLSA::GlobalRouterLSA ( + GlobalRouterLSA::SPFStatus status, + Ipv4Address linkStateId, + Ipv4Address advertisingRtr) +: + m_linkStateId(linkStateId), + m_advertisingRtr(advertisingRtr), + m_linkRecords(), + m_status(status) +{ + NS_DEBUG("GlobalRouterLSA::GlobalRouterLSA (" << status << ", " << + linkStateId << ", " << advertisingRtr << ")"); +} + +GlobalRouterLSA::GlobalRouterLSA (GlobalRouterLSA& lsa) + : m_linkStateId(lsa.m_linkStateId), m_advertisingRtr(lsa.m_advertisingRtr), + m_status(lsa.m_status) +{ + NS_ASSERT_MSG(IsEmpty(), + "GlobalRouterLSA::GlobalRouterLSA (): Non-empty LSA in constructor"); + CopyLinkRecords (lsa); +} + + GlobalRouterLSA& +GlobalRouterLSA::operator= (const GlobalRouterLSA& lsa) +{ + m_linkStateId = lsa.m_linkStateId; + m_advertisingRtr = lsa.m_advertisingRtr; + m_status = lsa.m_status; + + ClearLinkRecords (); + CopyLinkRecords (lsa); + return *this; +} + + void +GlobalRouterLSA::CopyLinkRecords (const GlobalRouterLSA& lsa) +{ + for (ListOfLinkRecords_t::const_iterator i = lsa.m_linkRecords.begin (); + i != lsa.m_linkRecords.end (); + i++) + { + GlobalRouterLinkRecord *pSrc = *i; + GlobalRouterLinkRecord *pDst = new GlobalRouterLinkRecord; + + pDst->SetLinkType (pSrc->GetLinkType ()); + pDst->SetLinkId (pSrc->GetLinkId ()); + pDst->SetLinkData (pSrc->GetLinkData ()); + + m_linkRecords.push_back(pDst); + pDst = 0; + } +} + +GlobalRouterLSA::~GlobalRouterLSA() +{ + NS_DEBUG("GlobalRouterLSA::~GlobalRouterLSA ()"); + ClearLinkRecords (); +} + + void +GlobalRouterLSA::ClearLinkRecords(void) +{ + for ( ListOfLinkRecords_t::iterator i = m_linkRecords.begin (); + i != m_linkRecords.end (); + i++) + { + NS_DEBUG("GlobalRouterLSA::ClearLinkRecords (): free link record"); + + GlobalRouterLinkRecord *p = *i; + delete p; + p = 0; + + *i = 0; + } + NS_DEBUG("GlobalRouterLSA::ClearLinkRecords(): clear list"); + m_linkRecords.clear(); +} + + uint32_t +GlobalRouterLSA::AddLinkRecord (GlobalRouterLinkRecord* lr) +{ + m_linkRecords.push_back (lr); + return m_linkRecords.size (); +} + + uint32_t +GlobalRouterLSA::GetNLinkRecords (void) const +{ + return m_linkRecords.size (); +} + + GlobalRouterLinkRecord * +GlobalRouterLSA::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, "GlobalRouterLSA::GetLinkRecord (): invalid index"); + return 0; +} + + bool +GlobalRouterLSA::IsEmpty (void) const +{ + return m_linkRecords.size () == 0; +} + + Ipv4Address +GlobalRouterLSA::GetLinkStateId (void) const +{ + return m_linkStateId; +} + + void +GlobalRouterLSA::SetLinkStateId (Ipv4Address addr) +{ + m_linkStateId = addr; +} + + Ipv4Address +GlobalRouterLSA::GetAdvertisingRouter (void) const +{ + return m_advertisingRtr; +} + + void +GlobalRouterLSA::SetAdvertisingRouter (Ipv4Address addr) +{ + m_advertisingRtr = addr; +} + + GlobalRouterLSA::SPFStatus +GlobalRouterLSA::GetStatus (void) const +{ + return m_status; +} + + void +GlobalRouterLSA::SetStatus (GlobalRouterLSA::SPFStatus status) +{ + m_status = status; +} + + void +GlobalRouterLSA::Print (std::ostream &os) const +{ + os << "m_linkStateId = " << m_linkStateId << std::endl << + "m_advertisingRtr = " << m_advertisingRtr << std::endl; + + for ( ListOfLinkRecords_t::const_iterator i = m_linkRecords.begin (); + i != m_linkRecords.end (); + i++) + { + GlobalRouterLinkRecord *p = *i; + os << "----------" << std::endl; + os << "m_linkId = " << p->GetLinkId () << std::endl; + os << "m_linkData = " << p->GetLinkData () << std::endl; + } +} + +std::ostream& operator<< (std::ostream& os, GlobalRouterLSA& 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::ClearLSAs () +{ + NS_DEBUG("GlobalRouter::ClearLSAs ()"); + + for ( ListOfLSAs_t::iterator i = m_LSAs.begin (); + i != m_LSAs.end (); + i++) + { + NS_DEBUG("GlobalRouter::ClearLSAs (): free LSA"); + + GlobalRouterLSA *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 ()"); + NS_ASSERT_MSG(m_node, + "GlobalRouter::DiscoverLSAs (): interface not set"); + + ClearLSAs (); +// +// 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"); +// +// We are, for now at least, only going to report RouterLSAs in this method. +// What this means is that there is going to be one advertisement with some +// number of link records. This means that GetNumLSAs will actually always +// return exactly one. +// + GlobalRouterLSA *pLSA = new GlobalRouterLSA; + pLSA->SetLinkStateId (m_routerId); + pLSA->SetAdvertisingRouter (m_routerId); + pLSA->SetStatus (GlobalRouterLSA::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.). So we have to walk through the list of net devices and +// pay attention to those that are directly connected to another router through +// a point-to-point channel. +// + uint32_t numDevices = m_node->GetNDevices(); + NS_DEBUG("GlobalRouter::DiscoverLSAs (): numDevices = " << numDevices); +// +// Loop through the devices looking for those connected to a point-to-point +// channel. +// + for (uint32_t i = 0; i < numDevices; ++i) + { + Ptr ndLocal = m_node->GetDevice(i); + + if (!ndLocal->IsPointToPoint ()) + { + NS_DEBUG("GlobalRouter::DiscoverLSAs (): non-point-to-point device"); + continue; + } + + NS_DEBUG("GlobalRouter::DiscoverLSAs (): 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. +// + GlobalRouterLinkRecord *plr = new GlobalRouterLinkRecord; + plr->SetLinkType (GlobalRouterLinkRecord::PointToPoint); + plr->SetLinkId (rtrIdRemote); + plr->SetLinkData (addrLocal); + pLSA->AddLinkRecord(plr); + plr = 0; + + plr = new GlobalRouterLinkRecord; + plr->SetLinkType (GlobalRouterLinkRecord::StubNetwork); + plr->SetLinkId (addrRemote); + plr->SetLinkData (Ipv4Address(maskRemote.GetHostOrder())); // Frown + pLSA->AddLinkRecord(plr); + plr = 0; + } +// +// 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); + return m_LSAs.size (); +} + + 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, GlobalRouterLSA &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) + { + GlobalRouterLSA *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 +{ + + uint32_t nDevices = ch->GetNDevices(); + NS_ASSERT_MSG(nDevices == 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..b2927d631 --- /dev/null +++ b/src/routing/global-routing/global-router-interface.h @@ -0,0 +1,580 @@ +/* -*- 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 GlobalRouterLinkRecord 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 GlobalRouterLinkRecord +{ +public: +/** + * @enum LinkType + * @brief Enumeration of the possible types of Global Router 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 Router 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. + */ + GlobalRouterLinkRecord (); + +/** + * Construct an initialized Global Router 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 + */ + GlobalRouterLinkRecord ( + LinkType linkType, + Ipv4Address linkId, + Ipv4Address linkData, + uint32_t metric); + +/** + * @brief Destroy a Global Router Link Record. + * + * Currently does nothing. Here as a placeholder only. + */ + ~GlobalRouterLinkRecord (); + +/** + * Get the Link ID field of the Global Router 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 Router 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 Router 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 Router 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 Router 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 Router Link Record. + */ + LinkType GetLinkType(void) const; + +/** + * @brief Set the Link Type field of the Global Router 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 Router Link Record. + */ + void SetLinkType(LinkType linkType); + +/** + * @brief Get the Metric Data field of the Global Router 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 Router Link Record. + */ + uint32_t GetMetric(void) const; + +/** + * @brief Set the Metric Data field of the Global Router 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 Router 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 Router 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 GlobalRouterLSA +{ +public: +/** + * @enum SPFStatus + * @brief Enumeration of the possible values of the status flag in the Router + * 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 Router Link State Advertisement. + * + * On completion Ipv4Address variables initialized to 0.0.0.0 and the + * list of Link State Records is empty. + */ + GlobalRouterLSA(); + +/** + * @brief Create an initialized Global Router 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. + */ + GlobalRouterLSA(SPFStatus status, Ipv4Address linkStateId, + Ipv4Address advertisingRtr); + +/** + * @brief Copy constructor for a Global Router 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. + */ + GlobalRouterLSA (GlobalRouterLSA& lsa); + +/** + * @brief Destroy an existing Global Router Link State Advertisement. + * + * Any Global Router Link Records present in the list are freed. + */ + ~GlobalRouterLSA(); + +/** + * @brief Assignment operator for a Global Router Link State Advertisement. + * + * Takes an existing Global Router Link State Advertisement and overwrites + * it to make a semantically identical copy of a given prototype LSA. + * + * If there are any Global Router 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. + */ + GlobalRouterLSA& operator= (const GlobalRouterLSA& lsa); + +/** + * @brief Copy any Global Router Link Records in a given Global Router 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 GlobalRouterLSA& lsa); + +/** + * @brief Add a given Global Router Link Record to the LSA. + * + * @param lr The Global Router Link Record to be added. + * @returns The number of link records in the list. + */ + uint32_t AddLinkRecord (GlobalRouterLinkRecord* lr); + +/** + * @brief Return the number of Global Router 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 Router Link Record. + * + * @param n The LSA number desired. + * @returns The number of link records in the list. + */ + GlobalRouterLinkRecord* GetLinkRecord (uint32_t n) const; + +/** + * @brief Release all of the Global Router Link Records present in the Global + * Router Link State Advertisement and make the list of link records empty. + */ + void ClearLinkRecords(void); + +/** + * @brief Check to see if the list of Global Router Link Records present in the + * Global Router 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 Router Link State Advertisement and + * any Global Router Link Records present in the list. Quite verbose. + */ + void Print (std::ostream &os) const; + +/** + * @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 GlobalRouter::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 GlobalRouter::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 GlobalRouter::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 GlobalRouter::GetRouterId () + */ + void SetAdvertisingRouter (Ipv4Address rtr); + +/** + * @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 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 GlobalRouter::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 GlobalRouter::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 GlobalRouter::DiscoverLSAs () + */ + ListOfLinkRecords_t m_linkRecords; + +/** + * 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, GlobalRouterLSA& 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 Router 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 GlobalRouterLSA + * @see GlobalRouter::GetLSA () + * @returns The number of Global Router Link State Advertisements. + */ + uint32_t DiscoverLSAs (void); + +/** + * @brief Get the Number of Global Router 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 GlobalRouter::DiscoverLSAs () + * @see GlobalRouter::GetLSA () + * @returns The number of Global Router Link State Advertisements. + */ + uint32_t GetNumLSAs (void) const; + +/** + * @brief Get a Global Router 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 Router Link + * State Advertisement into the requested GlobalRouterLSA 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 GlobalRouterLSA + * @see GlobalRouter::GetNumLSAs () + * @param n The index number of the LSA you want to read. + * @param lsa The GlobalRouterLSA class to receive the LSA information. + * @returns The number of Global Router Link State Advertisements. + */ + bool GetLSA (uint32_t n, GlobalRouterLSA &lsa) const; + +protected: + virtual ~GlobalRouter (); + void ClearLSAs (void); + + Ptr GetAdjacent(Ptr nd, Ptr ch) const; + uint32_t FindIfIndexForDevice(Ptr node, Ptr nd) const; + + Ptr m_node; + + typedef std::list ListOfLSAs_t; + ListOfLSAs_t m_LSAs; + + Ipv4Address m_routerId; + +private: +/** + * @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..787ef9308 --- /dev/null +++ b/src/routing/global-routing/wscript @@ -0,0 +1,20 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +def build(bld): + module = bld.create_obj('cpp', 'shlib') + module.name = 'ns3-global-routing' + module.target = module.name + module.uselib_local = ['ns3-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/wscript b/src/wscript index 32c7bdde0..aa57364be 100644 --- a/src/wscript +++ b/src/wscript @@ -18,10 +18,10 @@ all_modules = ( 'devices/point-to-point', 'devices/csma-cd', 'applications', + 'routing/global-routing', 'mobility', ) - def set_options(opt): opt.sub_options('simulator')