From dd90b30eaa18a58a1a3a665d92f4723f6b88d7fc Mon Sep 17 00:00:00 2001 From: Tommaso Pecorella Date: Thu, 25 Feb 2016 16:30:34 -0800 Subject: [PATCH] internet: RIPv2 implementation --- examples/routing/rip-simple-network.cc | 258 +++++ examples/routing/wscript | 6 + src/internet/doc/routing-overview.rst | 46 +- src/internet/helper/rip-helper.cc | 182 ++++ src/internet/helper/rip-helper.h | 148 +++ src/internet/model/rip-header.cc | 278 +++++ src/internet/model/rip-header.h | 256 +++++ src/internet/model/rip.cc | 1369 ++++++++++++++++++++++++ src/internet/model/rip.h | 409 +++++++ src/internet/test/ipv4-rip-test.cc | 647 +++++++++++ src/internet/wscript | 8 + 11 files changed, 3589 insertions(+), 18 deletions(-) create mode 100644 examples/routing/rip-simple-network.cc create mode 100644 src/internet/helper/rip-helper.cc create mode 100644 src/internet/helper/rip-helper.h create mode 100644 src/internet/model/rip-header.cc create mode 100644 src/internet/model/rip-header.h create mode 100644 src/internet/model/rip.cc create mode 100644 src/internet/model/rip.h create mode 100644 src/internet/test/ipv4-rip-test.cc diff --git a/examples/routing/rip-simple-network.cc b/examples/routing/rip-simple-network.cc new file mode 100644 index 000000000..f8c908659 --- /dev/null +++ b/examples/routing/rip-simple-network.cc @@ -0,0 +1,258 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +// Network topology +// +// SRC +// |<=== source network +// A-----B +// \ / \ all networks have cost 1, except +// \ / | for the direct link from C to D, which +// C / has cost 10 +// | / +// |/ +// D +// |<=== target network +// DST +// +// +// A, B, C and D are RIPng routers. +// A and D are configured with static addresses. +// SRC and DST will exchange packets. +// +// After about 3 seconds, the topology is built, and Echo Reply will be received. +// After 40 seconds, the link between B and D will break, causing a route failure. +// After 44 seconds from the failure, the routers will recovery from the failure. +// Split Horizoning should affect the recovery time, but it is not. See the manual +// for an explanation of this effect. +// +// If "showPings" is enabled, the user will see: +// 1) if the ping has been acknowledged +// 2) if a Destination Unreachable has been received by the sender +// 3) nothing, when the Echo Request has been received by the destination but +// the Echo Reply is unable to reach the sender. +// Examining the .pcap files with Wireshark can confirm this effect. + + +#include +#include "ns3/core-module.h" +#include "ns3/internet-module.h" +#include "ns3/csma-module.h" +#include "ns3/internet-apps-module.h" +#include "ns3/ipv4-static-routing-helper.h" +#include "ns3/ipv4-routing-table-entry.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("RipSimpleRouting"); + +void TearDownLink (Ptr nodeA, Ptr nodeB, uint32_t interfaceA, uint32_t interfaceB) +{ + nodeA->GetObject ()->SetDown (interfaceA); + nodeB->GetObject ()->SetDown (interfaceB); +} + +int main (int argc, char **argv) +{ + bool verbose = false; + bool printRoutingTables = false; + bool showPings = false; + std::string SplitHorizon ("PoisonReverse"); + + CommandLine cmd; + cmd.AddValue ("verbose", "turn on log components", verbose); + cmd.AddValue ("printRoutingTables", "Print routing tables at 30, 60 and 90 seconds", printRoutingTables); + cmd.AddValue ("showPings", "Show Ping6 reception", showPings); + cmd.AddValue ("splitHorizonStrategy", "Split Horizon strategy to use (NoSplitHorizon, SplitHorizon, PoisonReverse)", SplitHorizon); + cmd.Parse (argc, argv); + + if (verbose) + { + LogComponentEnableAll (LogLevel (LOG_PREFIX_TIME | LOG_PREFIX_NODE)); + LogComponentEnable ("RipSimpleRouting", LOG_LEVEL_INFO); + LogComponentEnable ("Rip", LOG_LEVEL_ALL); + LogComponentEnable ("Ipv4Interface", LOG_LEVEL_ALL); + LogComponentEnable ("Icmpv4L4Protocol", LOG_LEVEL_ALL); + LogComponentEnable ("Ipv4L3Protocol", LOG_LEVEL_ALL); + LogComponentEnable ("ArpCache", LOG_LEVEL_ALL); + LogComponentEnable ("V4Ping", LOG_LEVEL_ALL); + } + + if (SplitHorizon == "NoSplitHorizon") + { + Config::SetDefault ("ns3::Rip::SplitHorizon", EnumValue (RipNg::NO_SPLIT_HORIZON)); + } + else if (SplitHorizon == "SplitHorizon") + { + Config::SetDefault ("ns3::Rip::SplitHorizon", EnumValue (RipNg::SPLIT_HORIZON)); + } + else + { + Config::SetDefault ("ns3::Rip::SplitHorizon", EnumValue (RipNg::POISON_REVERSE)); + } + + NS_LOG_INFO ("Create nodes."); + Ptr src = CreateObject (); + Names::Add ("SrcNode", src); + Ptr dst = CreateObject (); + Names::Add ("DstNode", dst); + Ptr a = CreateObject (); + Names::Add ("RouterA", a); + Ptr b = CreateObject (); + Names::Add ("RouterB", b); + Ptr c = CreateObject (); + Names::Add ("RouterC", c); + Ptr d = CreateObject (); + Names::Add ("RouterD", d); + NodeContainer net1 (src, a); + NodeContainer net2 (a, b); + NodeContainer net3 (a, c); + NodeContainer net4 (b, c); + NodeContainer net5 (c, d); + NodeContainer net6 (b, d); + NodeContainer net7 (d, dst); + NodeContainer routers (a, b, c, d); + NodeContainer nodes (src, dst); + + + NS_LOG_INFO ("Create channels."); + CsmaHelper csma; + csma.SetChannelAttribute ("DataRate", DataRateValue (5000000)); + csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2))); + NetDeviceContainer ndc1 = csma.Install (net1); + NetDeviceContainer ndc2 = csma.Install (net2); + NetDeviceContainer ndc3 = csma.Install (net3); + NetDeviceContainer ndc4 = csma.Install (net4); + NetDeviceContainer ndc5 = csma.Install (net5); + NetDeviceContainer ndc6 = csma.Install (net6); + NetDeviceContainer ndc7 = csma.Install (net7); + + NS_LOG_INFO ("Create IPv4 and routing"); + RipHelper ripRouting; + + // Rule of thumb: + // Interfaces are added sequentially, starting from 0 + // However, interface 0 is always the loopback... + ripRouting.ExcludeInterface (a, 1); + ripRouting.ExcludeInterface (d, 3); + + ripRouting.SetInterfaceMetric (c, 3, 10); + ripRouting.SetInterfaceMetric (d, 1, 10); + + Ipv4ListRoutingHelper listRH; + listRH.Add (ripRouting, 0); +// Ipv4StaticRoutingHelper staticRh; +// listRH.Add (staticRh, 5); + + InternetStackHelper internet; + internet.SetIpv6StackInstall (false); + internet.SetRoutingHelper (listRH); + internet.Install (routers); + + InternetStackHelper internetNodes; + internetNodes.SetIpv6StackInstall (false); + internetNodes.Install (nodes); + + // Assign addresses. + // The source and destination networks have global addresses + // The "core" network just needs link-local addresses for routing. + // We assign global addresses to the routers as well to receive + // ICMPv6 errors. + NS_LOG_INFO ("Assign IPv4 Addresses."); + Ipv4AddressHelper ipv4; + + ipv4.SetBase (Ipv4Address ("10.0.0.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic1 = ipv4.Assign (ndc1); + + ipv4.SetBase (Ipv4Address ("10.0.1.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic2 = ipv4.Assign (ndc2); + + ipv4.SetBase (Ipv4Address ("10.0.2.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic3 = ipv4.Assign (ndc3); + + ipv4.SetBase (Ipv4Address ("10.0.3.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic4 = ipv4.Assign (ndc4); + + ipv4.SetBase (Ipv4Address ("10.0.4.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic5 = ipv4.Assign (ndc5); + + ipv4.SetBase (Ipv4Address ("10.0.5.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic6 = ipv4.Assign (ndc6); + + ipv4.SetBase (Ipv4Address ("10.0.6.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic7 = ipv4.Assign (ndc7); + + Ptr staticRouting; + staticRouting = Ipv4RoutingHelper::GetRouting (src->GetObject ()->GetRoutingProtocol ()); + staticRouting->SetDefaultRoute ("10.0.0.2", 1 ); + staticRouting = Ipv4RoutingHelper::GetRouting (dst->GetObject ()->GetRoutingProtocol ()); + staticRouting->SetDefaultRoute ("10.0.6.1", 1 ); + + if (printRoutingTables) + { + RipHelper routingHelper; + + Ptr routingStream = Create (&std::cout); + + routingHelper.PrintRoutingTableAt (Seconds (30.0), a, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (30.0), b, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (30.0), c, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (30.0), d, routingStream); + + routingHelper.PrintRoutingTableAt (Seconds (60.0), a, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (60.0), b, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (60.0), c, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (60.0), d, routingStream); + + routingHelper.PrintRoutingTableAt (Seconds (90.0), a, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (90.0), b, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (90.0), c, routingStream); + routingHelper.PrintRoutingTableAt (Seconds (90.0), d, routingStream); + } + + NS_LOG_INFO ("Create Applications."); + uint32_t packetSize = 1024; + Time interPacketInterval = Seconds (1.0); + V4PingHelper ping ("10.0.6.2"); + + ping.SetAttribute ("Interval", TimeValue (interPacketInterval)); + ping.SetAttribute ("Size", UintegerValue (packetSize)); + if (showPings) + { + ping.SetAttribute ("Verbose", BooleanValue (true)); + } + ApplicationContainer apps = ping.Install (src); + apps.Start (Seconds (1.0)); + apps.Stop (Seconds (110.0)); + + AsciiTraceHelper ascii; + csma.EnableAsciiAll (ascii.CreateFileStream ("rip-simple-routing.tr")); + csma.EnablePcapAll ("rip-simple-routing", true); + + Simulator::Schedule (Seconds (40), &TearDownLink, b, d, 3, 2); + + /* Now, do the actual simulation. */ + NS_LOG_INFO ("Run Simulation."); + Simulator::Stop (Seconds (131.0)); + Simulator::Run (); + Simulator::Destroy (); + NS_LOG_INFO ("Done."); +} + diff --git a/examples/routing/wscript b/examples/routing/wscript index 34f8bce9a..d0b5619c7 100644 --- a/examples/routing/wscript +++ b/examples/routing/wscript @@ -42,3 +42,9 @@ def build(bld): obj.source = 'ripng-simple-network.cc' bld.register_ns3_script('simple-routing-ping6.py', ['csma', 'internet', 'internet-apps']) + + obj = bld.create_ns3_program('rip-simple-network', + ['csma', 'internet', 'internet-apps']) + obj.source = 'rip-simple-network.cc' + + \ No newline at end of file diff --git a/src/internet/doc/routing-overview.rst b/src/internet/doc/routing-overview.rst index 6f0f6b6ea..744a020e6 100644 --- a/src/internet/doc/routing-overview.rst +++ b/src/internet/doc/routing-overview.rst @@ -215,7 +215,7 @@ is finally used to populate the routes themselves. Unicast routing *************** -There are presently seven unicast routing protocols defined for IPv4 and three for +There are presently eigth unicast routing protocols defined for IPv4 and three for IPv6: * class Ipv4StaticRouting (covering both unicast and multicast) @@ -230,6 +230,7 @@ IPv6: manager, if that is used) * class Ipv4NixVectorRouting (a more efficient version of global routing that stores source routes in a packet header field) +* class Rip - the IPv4 RIPv2 protocol (:rfc:`2453`) * class Ipv6ListRouting (used to store a prioritized list of routing protocols) * class Ipv6StaticRouting * class RipNg - the IPv6 RIPng protocol (:rfc:`2080`) @@ -334,27 +335,30 @@ respond to dynamic changes to a device's IP address or link up/down notifications; i.e. the topology changes are due to loss/gain of connectivity over a wireless channel. -RIPng -+++++ +RIP and RIPng ++++++++++++++ + +The RIPv2 protocol for IPv4 is described in the :rfc:`2453`, and it consolidates +a number of improvements over the base protocol defined in :rfc:`1058`. This IPv6 routing protocol (:rfc:`2080`) is the evolution of the well-known -RIPv1 anf RIPv2 (see :rfc:`1058` and :rfc:`1723`) routing protocols for IPv4. +RIPv1 (see :rfc:`1058` and :rfc:`1723`) routing protocol for IPv4. -The protocol is very simple, and it is normally suitable for flat, simple +The protocols are very simple, and are normally suitable for flat, simple network topologies. -RIPng is strongly based on RIPv1 and RIPv2, and it have the very same goals and -limitations. In particular, RIP considers any route with a metric equal or greater +RIPv1, RIPv2, and RIPng have the very same goals and limitations. +In particular, RIP considers any route with a metric equal or greater than 16 as unreachable. As a consequence, the maximum number of hops is the network must be less than 15 (the number of routers is not set). Users are encouraged to read :rfc:`2080` and :rfc:`1058` to fully understand -RIPng behaviour and limitations. +RIP behaviour and limitations. Routing convergence ~~~~~~~~~~~~~~~~~~~ -RIPng uses a Distance-Vector algorithm, and routes are updated according to +RIP uses a Distance-Vector algorithm, and routes are updated according to the Bellman-Ford algorithm (sometimes known as Ford-Fulkerson algorithm). The algorithm has a convergence time of O(\|V\|*\|E\|) where \|V\| and \|E\| are the number of vertices (routers) and edges (links) respectively. @@ -365,7 +369,7 @@ cooldown, the toplogy can require some time to be stabilized. Users should be aware that, during routing tables construction, the routers might drop packets. Data traffic should be sent only after a time long -enough to allow RIPng to build the network topology. +enough to allow RIP to build the network topology. Usually 80 seconds should be enough to have a suboptimal (but working) routing setup. This includes the time needed to propagate the routes to the most distant router (16 hops) with Triggered Updates. @@ -375,13 +379,15 @@ time might be quite high, and it might be even higher than the initial setup time. Moreover, the network topology recovery is affected by the Split Horizoning strategy. -The example ``examples/routing/ripng-simple-network.cc`` shows both the -network setup and network recovery phases. +The examples ``examples/routing/ripng-simple-network.cc`` and +``examples/routing/rip-simple-network.cc`` +shows both the network setup and network recovery phases. Split Horizoning ~~~~~~~~~~~~~~~~ -Split Horizon is a strategy to prevent routing instability. Three options are possible: +Split Horizon is a strategy to prevent routing instability. +Three options are possible: * No Split Horizon * Split Horizon @@ -394,7 +400,7 @@ Poison Reverse will advertise the route on the interface from which it was learned, but with a metric of 16 (infinity). For a full analysis of the three techniques, see :rfc:`1058`, section 2.2. -The example ``ripng-simple-network.cc`` is based on the network toplogy +The examples are based on the network toplogy described in the RFC, but it does not show the effect described there. The reason are the Triggered Updates, together with the fact that when a @@ -410,18 +416,19 @@ remanins valid. Default routes ~~~~~~~~~~~~~~ -RIPng protocol should be installed *only* on routers. As a consequence, +RIP protocol should be installed *only* on routers. As a consequence, nodes will not know what is the default router. To overcome this limitation, users should either install the default route -manually (e.g., by resorting to Ipv6StaticRouting), or by using RADVd. +manually (e.g., by resorting to Ipv4StaticRouting or Ipv6StaticRouting), or +by using RADVd (in case of IPv6). RADVd is available in |ns3| in the Applications module, and it is strongly suggested. Protocol parameters and options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The RIPng |ns3| implementation allows to change all the timers associated +The RIP |ns3| implementations allow to change all the timers associated with route updates and routes lifetime. Moreover, users can change the interface metrics on a per-node basis. @@ -431,11 +438,14 @@ selected on a per-node basis, with the choices being "no split horizon", "split horizon" and "poison reverse". See :rfc:`2080` for further details, and :rfc:`1058` for a complete discussion on the split horizoning strategies. +Moreover, it is possible to use a non-standard value for Link Down Value (i.e., +the value after which a link is considered down). The defaul is value is 16. + Limitations ~~~~~~~~~~~ There is no support for the Next Hop option (:rfc:`2080`, Section 2.1.1). -The Next Hop option is useful when RIPng is not being run on all of the +The Next Hop option is useful when RIP is not being run on all of the routers on a network. Support for this option may be considered in the future. diff --git a/src/internet/helper/rip-helper.cc b/src/internet/helper/rip-helper.cc new file mode 100644 index 000000000..f13aa6667 --- /dev/null +++ b/src/internet/helper/rip-helper.cc @@ -0,0 +1,182 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#include "ns3/node.h" +#include "ns3/node-list.h" +#include "ns3/ipv4-list-routing.h" +#include "ns3/rip.h" +#include "rip-helper.h" + +namespace ns3 { + +RipHelper::RipHelper () +{ + m_factory.SetTypeId ("ns3::Rip"); +} + +RipHelper::RipHelper (const RipHelper &o) + : m_factory (o.m_factory) +{ + m_interfaceExclusions = o.m_interfaceExclusions; + m_interfaceMetrics = o.m_interfaceMetrics; +} + +RipHelper::~RipHelper () +{ + m_interfaceExclusions.clear (); + m_interfaceMetrics.clear (); +} + +RipHelper* +RipHelper::Copy (void) const +{ + return new RipHelper (*this); +} + +Ptr +RipHelper::Create (Ptr node) const +{ + Ptr rip = m_factory.Create (); + + std::map, std::set >::const_iterator it = m_interfaceExclusions.find (node); + + if(it != m_interfaceExclusions.end ()) + { + rip->SetInterfaceExclusions (it->second); + } + + std::map< Ptr, std::map >::const_iterator iter = m_interfaceMetrics.find (node); + + if(iter != m_interfaceMetrics.end ()) + { + std::map::const_iterator subiter; + for (subiter = iter->second.begin (); subiter != iter->second.end (); subiter++) + { + rip->SetInterfaceMetric (subiter->first, subiter->second); + } + } + + node->AggregateObject (rip); + return rip; +} + +void +RipHelper::Set (std::string name, const AttributeValue &value) +{ + m_factory.Set (name, value); +} + + +int64_t +RipHelper::AssignStreams (NodeContainer c, int64_t stream) +{ + int64_t currentStream = stream; + Ptr node; + for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i) + { + node = (*i); + Ptr ipv4 = node->GetObject (); + NS_ASSERT_MSG (ipv4, "Ipv4 not installed on node"); + Ptr proto = ipv4->GetRoutingProtocol (); + NS_ASSERT_MSG (proto, "Ipv4 routing not installed on node"); + Ptr rip = DynamicCast (proto); + if (rip) + { + currentStream += rip->AssignStreams (currentStream); + continue; + } + // RIP may also be in a list + Ptr list = DynamicCast (proto); + if (list) + { + int16_t priority; + Ptr listProto; + Ptr listRip; + for (uint32_t i = 0; i < list->GetNRoutingProtocols (); i++) + { + listProto = list->GetRoutingProtocol (i, priority); + listRip = DynamicCast (listProto); + if (listRip) + { + currentStream += listRip->AssignStreams (currentStream); + break; + } + } + } + } + return (currentStream - stream); +} + +void RipHelper::SetDefaultRouter (Ptr node, Ipv4Address nextHop, uint32_t interface) +{ + Ptr ipv4 = node->GetObject (); + NS_ASSERT_MSG (ipv4, "Ipv4 not installed on node"); + Ptr proto = ipv4->GetRoutingProtocol (); + NS_ASSERT_MSG (proto, "Ipv4 routing not installed on node"); + Ptr rip = DynamicCast (proto); + if (rip) + { + rip->AddDefaultRouteTo (nextHop, interface); + } + // RIP may also be in a list + Ptr list = DynamicCast (proto); + if (list) + { + int16_t priority; + Ptr listProto; + Ptr listRip; + for (uint32_t i = 0; i < list->GetNRoutingProtocols (); i++) + { + listProto = list->GetRoutingProtocol (i, priority); + listRip = DynamicCast (listProto); + if (listRip) + { + listRip->AddDefaultRouteTo (nextHop, interface); + break; + } + } + } +} + +void +RipHelper::ExcludeInterface (Ptr node, uint32_t interface) +{ + std::map< Ptr, std::set >::iterator it = m_interfaceExclusions.find (node); + + if (it == m_interfaceExclusions.end ()) + { + std::set interfaces; + interfaces.insert (interface); + + m_interfaceExclusions.insert (std::make_pair (node, interfaces)); + } + else + { + it->second.insert (interface); + } +} + +void RipHelper::SetInterfaceMetric (Ptr node, uint32_t interface, uint8_t metric) +{ + m_interfaceMetrics[node][interface] = metric; +} + +} + diff --git a/src/internet/helper/rip-helper.h b/src/internet/helper/rip-helper.h new file mode 100644 index 000000000..15636bed2 --- /dev/null +++ b/src/internet/helper/rip-helper.h @@ -0,0 +1,148 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#ifndef RIP_HELPER_H +#define RIP_HELPER_H + +#include "ns3/object-factory.h" +#include "ns3/ipv4-routing-helper.h" +#include "ns3/node-container.h" +#include "ns3/node.h" + +namespace ns3 { + +/** + * \brief Helper class that adds RIP routing to nodes. + * + * This class is expected to be used in conjunction with + * ns3::InternetStackHelper::SetRoutingHelper + * + */ +class RipHelper : public Ipv4RoutingHelper +{ +public: + /* + * Construct an RipHelper to make life easier while adding RIP + * routing to nodes. + */ + RipHelper (); + + /** + * \brief Construct an RipHelper from another previously + * initialized instance (Copy Constructor). + */ + RipHelper (const RipHelper &); + + virtual ~RipHelper (); + + /** + * \returns pointer to clone of this RipHelper + * + * This method is mainly for internal use by the other helpers; + * clients are expected to free the dynamic memory allocated by this method + */ + RipHelper* Copy (void) const; + + /** + * \param node the node on which the routing protocol will run + * \returns a newly-created routing protocol + * + * This method will be called by ns3::InternetStackHelper::Install + */ + virtual Ptr Create (Ptr node) const; + + /** + * \param name the name of the attribute to set + * \param value the value of the attribute to set. + * + * This method controls the attributes of ns3::Ripng + */ + void Set (std::string name, const AttributeValue &value); + + /** + * Assign a fixed random variable stream number to the random variables + * used by this model. Return the number of streams (possibly zero) that + * have been assigned. The Install() method should have previously been + * called by the user. + * + * \param c NetDeviceContainer of the set of net devices for which the + * SixLowPanNetDevice should be modified to use a fixed stream + * \param stream first stream index to use + * \return the number of stream indices assigned by this helper + */ + int64_t AssignStreams (NodeContainer c, int64_t stream); + + /** + * \brief Install a default route in the node. + * + * The traffic will be routed to the nextHop, located on the specified + * interface, unless a more specific route is found. + * + * \param node the node + * \param nextHop the next hop + * \param interface the network interface + */ + void SetDefaultRouter (Ptr node, Ipv4Address nextHop, uint32_t interface); + + /** + * \brief Exclude an interface from RIP protocol. + * + * You have to call this function \a before installing RIP in the nodes. + * + * Note: the exclusion means that RIP will not be propagated on that interface. + * The network prefix on that interface will be still considered in RIP. + * + * \param node the node + * \param interface the network interface to be excluded + */ + void ExcludeInterface (Ptr node, uint32_t interface); + + /** + * \brief Set a metric for an interface. + * + * You have to call this function \a before installing RIP in the nodes. + * + * Note: RIP will apply the metric on route message reception. + * As a consequence, interface metric should be set on the receiver. + * + * \param node the node + * \param interface the network interface + * \param metric the interface metric + */ + void SetInterfaceMetric (Ptr node, uint32_t interface, uint8_t metric); + +private: + /** + * \brief Assignment operator declared private and not implemented to disallow + * assignment and prevent the compiler from happily inserting its own. + */ + RipHelper &operator = (const RipHelper &o); + + ObjectFactory m_factory; //!< Object Factory + + std::map< Ptr, std::set > m_interfaceExclusions; //!< Interface Exclusion set + std::map< Ptr, std::map > m_interfaceMetrics; //!< Interface Metric set +}; + +} // namespace ns3 + + +#endif /* RIP_HELPER_H */ + diff --git a/src/internet/model/rip-header.cc b/src/internet/model/rip-header.cc new file mode 100644 index 000000000..5631f0b68 --- /dev/null +++ b/src/internet/model/rip-header.cc @@ -0,0 +1,278 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#include "rip-header.h" + +namespace ns3 { + +/* + * RipRte + */ +NS_OBJECT_ENSURE_REGISTERED (RipRte); + + +RipRte::RipRte () + : m_tag (0), m_prefix ("127.0.0.1"), m_subnetMask ("0.0.0.0"), m_nextHop ("0.0.0.0"), m_metric (16) +{ +} + +TypeId RipRte::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::RipRte") + .SetParent
() + .SetGroupName ("Internet") + .AddConstructor (); + return tid; +} + +TypeId RipRte::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +void RipRte::Print (std::ostream & os) const +{ + os << "prefix " << m_prefix << "/" << m_subnetMask.GetPrefixLength () << " Metric " << int(m_metric); + os << " Tag " << int(m_tag) << " Next Hop " << m_nextHop; +} + +uint32_t RipRte::GetSerializedSize () const +{ + return 20; +} + +void RipRte::Serialize (Buffer::Iterator i) const +{ + i.WriteHtonU16 (2); + i.WriteHtonU16 (m_tag); + + i.WriteHtonU32 (m_prefix.Get ()); + i.WriteHtonU32 (m_subnetMask.Get ()); + i.WriteHtonU32 (m_nextHop.Get ()); + i.WriteHtonU32 (m_metric); +} + +uint32_t RipRte::Deserialize (Buffer::Iterator i) +{ + uint16_t tmp; + + tmp = i.ReadNtohU16 (); + if (tmp != 2) + { + return 0; + } + + m_tag = i.ReadNtohU16 (); + m_prefix.Set (i.ReadNtohU32 ()); + m_subnetMask.Set (i.ReadNtohU32 ()); + m_nextHop.Set (i.ReadNtohU32 ()); + + m_metric = i.ReadNtohU32 (); + + return GetSerializedSize (); +} + +void RipRte::SetPrefix (Ipv4Address prefix) +{ + m_prefix = prefix; +} + +Ipv4Address RipRte::GetPrefix () const +{ + return m_prefix; +} + +void RipRte::SetSubnetMask (Ipv4Mask subnetMask) +{ + m_subnetMask = subnetMask; +} + +Ipv4Mask RipRte::GetSubnetMask () const +{ + return m_subnetMask; +} + +void RipRte::SetRouteTag (uint16_t routeTag) +{ + m_tag = routeTag; +} + +uint16_t RipRte::GetRouteTag () const +{ + return m_tag; +} + +void RipRte::SetRouteMetric (uint32_t routeMetric) +{ + m_metric = routeMetric; +} + +uint32_t RipRte::GetRouteMetric () const +{ + return m_metric; +} + +void RipRte::SetNextHop (Ipv4Address nextHop) +{ + m_nextHop = nextHop; +} + +Ipv4Address RipRte::GetNextHop () const +{ + return m_nextHop; +} + + +std::ostream & operator << (std::ostream & os, const RipRte & h) +{ + h.Print (os); + return os; +} + +/* + * RipHeader + */ +NS_OBJECT_ENSURE_REGISTERED (RipHeader) + ; + +RipHeader::RipHeader () + : m_command (0) +{ +} + +TypeId RipHeader::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::RipHeader") + .SetParent
() + .SetGroupName ("Internet") + .AddConstructor (); + return tid; +} + +TypeId RipHeader::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +void RipHeader::Print (std::ostream & os) const +{ + os << "command " << int(m_command); + for (std::list::const_iterator iter = m_rteList.begin (); + iter != m_rteList.end (); iter ++) + { + os << " | "; + iter->Print (os); + } +} + +uint32_t RipHeader::GetSerializedSize () const +{ + RipRte rte; + return 4 + m_rteList.size () * rte.GetSerializedSize (); +} + +void RipHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + + i.WriteU8 (uint8_t (m_command)); + i.WriteU8 (1); + i.WriteU16 (0); + + for (std::list::const_iterator iter = m_rteList.begin (); + iter != m_rteList.end (); iter ++) + { + iter->Serialize (i); + i.Next(iter->GetSerializedSize ()); + } +} + +uint32_t RipHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + + uint8_t temp; + temp = i.ReadU8 (); + if ((temp == REQUEST) || (temp == RESPONSE)) + { + m_command = temp; + } + else + { + return 0; + } + + temp = i.ReadU8 (); + NS_ASSERT_MSG (temp == 1, "RIP received a message with mismatch version, aborting."); + + uint16_t temp16 = i.ReadU16 (); + NS_ASSERT_MSG (temp16 == 0, "RIP received a message with invalid filled flags, aborting."); + + uint8_t rteNumber = (i.GetSize () - 4)/20; + for (uint8_t n=0; n RipHeader::GetRteList (void) const +{ + return m_rteList; +} + + +std::ostream & operator << (std::ostream & os, const RipHeader & h) +{ + h.Print (os); + return os; +} + + +} + diff --git a/src/internet/model/rip-header.h b/src/internet/model/rip-header.h new file mode 100644 index 000000000..547bfbc7d --- /dev/null +++ b/src/internet/model/rip-header.h @@ -0,0 +1,256 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#ifndef RIP_HEADER_H +#define RIP_HEADER_H + +#include +#include "ns3/header.h" +#include "ns3/ipv4-address.h" +#include "ns3/packet.h" +#include "ns3/ipv4-header.h" + + +namespace ns3 { + +/** + * \ingroup rip + * \brief Rip v2 Routing Table Entry (RTE) - see \RFC{2453}. + */ +class RipRte : public Header +{ +public: + RipRte (void); + + /** + * \brief Get the type ID. + * \return The object TypeId. + */ + static TypeId GetTypeId (void); + + /** + * \brief Return the instance type identifier. + * \return Instance type ID. + */ + virtual TypeId GetInstanceTypeId (void) const; + + virtual void Print (std::ostream& os) const; + + /** + * \brief Get the serialized size of the packet. + * \return Size. + */ + virtual uint32_t GetSerializedSize (void) const; + + /** + * \brief Serialize the packet. + * \param start Buffer iterator. + */ + virtual void Serialize (Buffer::Iterator start) const; + + /** + * \brief Deserialize the packet. + * \param start Buffer iterator. + * \return Size of the packet. + */ + virtual uint32_t Deserialize (Buffer::Iterator start); + + /** + * \brief Set the prefix. + * \param prefix The prefix. + */ + void SetPrefix (Ipv4Address prefix); + + /** + * \brief Get the prefix. + * \returns The prefix. + */ + Ipv4Address GetPrefix (void) const; + + /** + * \brief Set the subnet mask. + * \param subnetMask The subnet mask. + */ + void SetSubnetMask (Ipv4Mask subnetMask); + + /** + * \brief Get the subnet mask. + * \returns The subnet mask. + */ + Ipv4Mask GetSubnetMask (void) const; + + /** + * \brief Set the route tag. + * \param routeTag The route tag. + */ + void SetRouteTag (uint16_t routeTag); + + /** + * \brief Get the route tag. + * \returns The route tag. + */ + uint16_t GetRouteTag (void) const; + + /** + * \brief Set the route metric. + * \param routeMetric The route metric. + */ + void SetRouteMetric (uint32_t routeMetric); + + /** + * \brief Get the route metric. + * \returns The route metric. + */ + uint32_t GetRouteMetric (void) const; + + /** + * \brief Set the route metric. + * \param routeMetric The route metric. + */ + void SetNextHop (Ipv4Address nextHop); + + /** + * \brief Get the route metric. + * \returns The route metric. + */ + Ipv4Address GetNextHop (void) const; + + +private: + uint16_t m_tag; //!< Route tag. + Ipv4Address m_prefix; //!< Advertised prefix. + Ipv4Mask m_subnetMask; //!< Subnet mask. + Ipv4Address m_nextHop; //!< Next hop. + uint32_t m_metric; //!< Route metric. +}; + +/** + * \brief Stream insertion operator. + * + * \param os the reference to the output stream + * \param h the Routing Table Entry + * \returns the reference to the output stream + */ +std::ostream & operator << (std::ostream & os, const RipRte & h); + +/** + * \ingroup rip + * \brief RipHeader - see \RFC{2453} + */ +class RipHeader : public Header +{ +public: + RipHeader (void); + + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + + /** + * \brief Return the instance type identifier. + * \return instance type ID + */ + virtual TypeId GetInstanceTypeId (void) const; + + virtual void Print (std::ostream& os) const; + + /** + * \brief Get the serialized size of the packet. + * \return size + */ + virtual uint32_t GetSerializedSize (void) const; + + /** + * \brief Serialize the packet. + * \param start Buffer iterator + */ + virtual void Serialize (Buffer::Iterator start) const; + + /** + * \brief Deserialize the packet. + * \param start Buffer iterator + * \return size of the packet + */ + virtual uint32_t Deserialize (Buffer::Iterator start); + + /** + * Commands to be used in Rip headers + */ + enum Command_e + { + REQUEST = 0x1, + RESPONSE = 0x2, + }; + + /** + * \brief Set the command + * \param command the command + */ + void SetCommand (Command_e command); + + /** + * \brief Get the command + * \returns the command + */ + Command_e GetCommand (void) const; + + /** + * \brief Add a RTE to the message + * \param rte the RTE + */ + void AddRte (RipRte rte); + + /** + * \brief Clear all the RTEs from the header + */ + void ClearRtes (); + + /** + * \brief Get the number of RTE included in the message + * \returns the number of RTE in the message + */ + uint16_t GetRteNumber (void) const; + + /** + * \brief Get the list of the RTEs included in the message + * \returns the list of the RTEs in the message + */ + std::list GetRteList (void) const; + +private: + uint8_t m_command; //!< command type + std::list m_rteList; //!< list of the RTEs in the message +}; + +/** + * \brief Stream insertion operator. + * + * \param os the reference to the output stream + * \param h the Rip header + * \returns the reference to the output stream + */ +std::ostream & operator << (std::ostream & os, const RipHeader & h); + +} + +#endif /* Rip_HEADER_H */ + diff --git a/src/internet/model/rip.cc b/src/internet/model/rip.cc new file mode 100644 index 000000000..d0d23c5c1 --- /dev/null +++ b/src/internet/model/rip.cc @@ -0,0 +1,1369 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#include +#include "rip.h" +#include "ns3/log.h" +#include "ns3/abort.h" +#include "ns3/assert.h" +#include "ns3/unused.h" +#include "ns3/random-variable-stream.h" +#include "ns3/ipv4-route.h" +#include "ns3/node.h" +#include "ns3/names.h" +#include "ns3/rip-header.h" +#include "ns3/udp-header.h" +#include "ns3/enum.h" +#include "ns3/uinteger.h" +#include "ns3/ipv4-packet-info-tag.h" +#include "ns3/loopback-net-device.h" + +#define RIP_ALL_NODE "224.0.0.9" +#define RIP_PORT 520 + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("Rip"); + +NS_OBJECT_ENSURE_REGISTERED (Rip); + +Rip::Rip () + : m_ipv4 (0), m_splitHorizonStrategy (Rip::POISON_REVERSE), m_initialized (false) +{ + m_rng = CreateObject (); +} + +Rip::~Rip () +{ +} + +TypeId +Rip::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::Rip") + .SetParent () + .SetGroupName ("Internet") + .AddConstructor () + .AddAttribute ("UnsolicitedRoutingUpdate", "The time between two Unsolicited Routing Updates.", + TimeValue (Seconds(30)), + MakeTimeAccessor (&Rip::m_unsolicitedUpdate), + MakeTimeChecker ()) + .AddAttribute ("StartupDelay", "Maximum random delay for protocol startup (send route requests).", + TimeValue (Seconds(1)), + MakeTimeAccessor (&Rip::m_startupDelay), + MakeTimeChecker ()) + .AddAttribute ("TimeoutDelay", "The delay to invalidate a route.", + TimeValue (Seconds(180)), + MakeTimeAccessor (&Rip::m_timeoutDelay), + MakeTimeChecker ()) + .AddAttribute ("GarbageCollectionDelay", "The delay to delete an expired route.", + TimeValue (Seconds(120)), + MakeTimeAccessor (&Rip::m_garbageCollectionDelay), + MakeTimeChecker ()) + .AddAttribute ("MinTriggeredCooldown", "Min cooldown delay after a Triggered Update.", + TimeValue (Seconds(1)), + MakeTimeAccessor (&Rip::m_minTriggeredUpdateDelay), + MakeTimeChecker ()) + .AddAttribute ("MaxTriggeredCooldown", "Max cooldown delay after a Triggered Update.", + TimeValue (Seconds(5)), + MakeTimeAccessor (&Rip::m_maxTriggeredUpdateDelay), + MakeTimeChecker ()) + .AddAttribute ("SplitHorizon", "Split Horizon strategy.", + EnumValue (Rip::POISON_REVERSE), + MakeEnumAccessor (&Rip::m_splitHorizonStrategy), + MakeEnumChecker (Rip::NO_SPLIT_HORIZON, "NoSplitHorizon", + Rip::SPLIT_HORIZON, "SplitHorizon", + Rip::POISON_REVERSE, "PoisonReverse")) + .AddAttribute ("LinkDownValue", "Value for link down in count to infinity.", + UintegerValue (16), + MakeUintegerAccessor (&Rip::m_linkDown), + MakeUintegerChecker ()) + ; + return tid; +} + +int64_t Rip::AssignStreams (int64_t stream) +{ + NS_LOG_FUNCTION (this << stream); + + m_rng->SetStream (stream); + return 1; +} + +void Rip::DoInitialize () +{ + NS_LOG_FUNCTION (this); + + bool addedGlobal = false; + + m_initialized = true; + + Time delay = m_unsolicitedUpdate + Seconds (m_rng->GetValue (0, 0.5*m_unsolicitedUpdate.GetSeconds ()) ); + m_nextUnsolicitedUpdate = Simulator::Schedule (delay, &Rip::SendUnsolicitedRouteUpdate, this); + + + for (uint32_t i = 0 ; i < m_ipv4->GetNInterfaces (); i++) + { + Ptr check = DynamicCast (m_ipv4->GetNetDevice (i)); + if (check) + { + continue; + } + + bool activeInterface = false; + if (m_interfaceExclusions.find (i) == m_interfaceExclusions.end ()) + { + activeInterface = true; + m_ipv4->SetForwarding (i, true); + } + + for (uint32_t j = 0; j < m_ipv4->GetNAddresses (i); j++) + { + Ipv4InterfaceAddress address = m_ipv4->GetAddress (i, j); + if (address.GetScope() != Ipv4InterfaceAddress::HOST && activeInterface == true) + { + NS_LOG_LOGIC ("RIP: adding socket to " << address.GetLocal ()); + TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory"); + Ptr theNode = GetObject (); + Ptr socket = Socket::CreateSocket (theNode, tid); + InetSocketAddress local = InetSocketAddress (address.GetLocal (), RIP_PORT); + int ret = socket->Bind (local); + NS_ASSERT_MSG (ret == 0, "Bind unsuccessful"); + socket->BindToNetDevice (m_ipv4->GetNetDevice (i)); + socket->SetIpRecvTtl (true); + m_sendSocketList[socket] = i; + } + else if (m_ipv4->GetAddress (i, j).GetScope() == Ipv4InterfaceAddress::GLOBAL) + { + addedGlobal = true; + } + } + } + + if (!m_recvSocket) + { + NS_LOG_LOGIC ("RIP: adding receiving socket"); + TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory"); + Ptr theNode = GetObject (); + m_recvSocket = Socket::CreateSocket (theNode, tid); + InetSocketAddress local = InetSocketAddress (RIP_ALL_NODE, RIP_PORT); + m_recvSocket->Bind (local); + m_recvSocket->SetRecvCallback (MakeCallback (&Rip::Receive, this)); + m_recvSocket->SetIpRecvTtl (true); + m_recvSocket->SetRecvPktInfo (true); + } + + + if (addedGlobal) + { + Time delay = Seconds (m_rng->GetValue (m_minTriggeredUpdateDelay.GetSeconds (), m_maxTriggeredUpdateDelay.GetSeconds ())); + m_nextTriggeredUpdate = Simulator::Schedule (delay, &Rip::DoSendRouteUpdate, this, false); + } + + delay = Seconds (m_rng->GetValue (0.01, m_startupDelay.GetSeconds ())); + m_nextTriggeredUpdate = Simulator::Schedule (delay, &Rip::SendRouteRequest, this); + + Ipv4RoutingProtocol::DoInitialize (); +} + +Ptr Rip::RouteOutput (Ptr p, const Ipv4Header &header, Ptr oif, Socket::SocketErrno &sockerr) +{ + NS_LOG_FUNCTION (this << header << oif); + + Ipv4Address destination = header.GetDestination (); + Ptr rtentry = 0; + + if (destination.IsMulticast ()) + { + // Note: Multicast routes for outbound packets are stored in the + // normal unicast table. An implication of this is that it is not + // possible to source multicast datagrams on multiple interfaces. + // This is a well-known property of sockets implementation on + // many Unix variants. + // So, we just log it and fall through to LookupStatic () + NS_LOG_LOGIC ("RouteOutput (): Multicast destination"); + } + + rtentry = Lookup (destination, oif); + if (rtentry) + { + sockerr = Socket::ERROR_NOTERROR; + } + else + { + sockerr = Socket::ERROR_NOROUTETOHOST; + } + return rtentry; +} + +bool Rip::RouteInput (Ptr p, const Ipv4Header &header, Ptr idev, + UnicastForwardCallback ucb, MulticastForwardCallback mcb, + LocalDeliverCallback lcb, ErrorCallback ecb) +{ + NS_LOG_FUNCTION (this << p << header << header.GetSource () << header.GetDestination () << idev); + + NS_ASSERT (m_ipv4 != 0); + // Check if input device supports IP + NS_ASSERT (m_ipv4->GetInterfaceForDevice (idev) >= 0); + uint32_t iif = m_ipv4->GetInterfaceForDevice (idev); + Ipv4Address dst = header.GetDestination (); + + if (m_ipv4->IsDestinationAddress (header.GetDestination (), iif)) + { + if (!lcb.IsNull ()) + { + NS_LOG_LOGIC ("Local delivery to " << header.GetDestination ()); + lcb (p, header, iif); + return true; + } + else + { + // The local delivery callback is null. This may be a multicast + // or broadcast packet, so return false so that another + // multicast routing protocol can handle it. It should be possible + // to extend this to explicitly check whether it is a unicast + // packet, and invoke the error callback if so + return false; + } + } + + if (dst.IsMulticast ()) + { + NS_LOG_LOGIC ("Multicast route not supported by RIP"); + return false; // Let other routing protocols try to handle this + } + + if (header.GetDestination ().IsBroadcast ()) + { + NS_LOG_LOGIC ("Dropping packet not for me and with dst Broadcast"); + if (!ecb.IsNull ()) + { + ecb (p, header, Socket::ERROR_NOROUTETOHOST); + } + return false; + } + + // Check if input device supports IP forwarding + if (m_ipv4->IsForwarding (iif) == false) + { + NS_LOG_LOGIC ("Forwarding disabled for this interface"); + if (!ecb.IsNull ()) + { + ecb (p, header, Socket::ERROR_NOROUTETOHOST); + } + return false; + } + // Next, try to find a route + NS_LOG_LOGIC ("Unicast destination"); + Ptr rtentry = Lookup (header.GetDestination ()); + + if (rtentry != 0) + { + NS_LOG_LOGIC ("Found unicast destination - calling unicast callback"); + ucb (rtentry, p, header); // unicast forwarding callback + return true; + } + else + { + NS_LOG_LOGIC ("Did not find unicast destination - returning false"); + return false; // Let other routing protocols try to handle this + } +} + +void Rip::NotifyInterfaceUp (uint32_t i) +{ + NS_LOG_FUNCTION (this << i); + + Ptr check = DynamicCast (m_ipv4->GetNetDevice (i)); + if (check) + { + return; + } + + for (uint32_t j = 0; j < m_ipv4->GetNAddresses (i); j++) + { + Ipv4InterfaceAddress address = m_ipv4->GetAddress (i, j); + Ipv4Mask networkMask = address.GetMask (); + Ipv4Address networkAddress = address.GetLocal ().CombineMask (networkMask); + + if (address.GetScope () == Ipv4InterfaceAddress::GLOBAL) + { + AddNetworkRouteTo (networkAddress, networkMask, i); + } + } + + if (!m_initialized) + { + return; + } + + + bool sendSocketFound = false; + for (SocketListI iter = m_sendSocketList.begin (); iter != m_sendSocketList.end (); iter++ ) + { + if (iter->second == i) + { + sendSocketFound = true; + break; + } + } + + bool activeInterface = false; + if (m_interfaceExclusions.find (i) == m_interfaceExclusions.end ()) + { + activeInterface = true; + m_ipv4->SetForwarding (i, true); + } + + for (uint32_t j = 0; j < m_ipv4->GetNAddresses (i); j++) + { + Ipv4InterfaceAddress address = m_ipv4->GetAddress (i, j); + + if (address.GetScope() != Ipv4InterfaceAddress::HOST && sendSocketFound == false && activeInterface == true) + { + NS_LOG_LOGIC ("RIP: adding sending socket to " << address.GetLocal ()); + TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory"); + Ptr theNode = GetObject (); + Ptr socket = Socket::CreateSocket (theNode, tid); + InetSocketAddress local = InetSocketAddress (address.GetLocal (), RIP_PORT); + socket->Bind (local); + socket->BindToNetDevice (m_ipv4->GetNetDevice (i)); + socket->SetIpRecvTtl (true); + m_sendSocketList[socket] = i; + } + if (address.GetScope () == Ipv4InterfaceAddress::GLOBAL) + { + SendTriggeredRouteUpdate (); + } + } + + if (!m_recvSocket) + { + NS_LOG_LOGIC ("RIP: adding receiving socket"); + TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory"); + Ptr theNode = GetObject (); + m_recvSocket = Socket::CreateSocket (theNode, tid); + InetSocketAddress local = InetSocketAddress (RIP_ALL_NODE, RIP_PORT); + m_recvSocket->Bind (local); + m_recvSocket->SetRecvCallback (MakeCallback (&Rip::Receive, this)); + m_recvSocket->SetIpRecvTtl (true); + m_recvSocket->SetRecvPktInfo (true); + } +} + +void Rip::NotifyInterfaceDown (uint32_t interface) +{ + NS_LOG_FUNCTION (this << interface); + + /* remove all routes that are going through this interface */ + for (RoutesI it = m_routes.begin (); it != m_routes.end (); it++) + { + if (it->first->GetInterface () == interface) + { + InvalidateRoute (it->first); + } + } + + for (SocketListI iter = m_sendSocketList.begin (); iter != m_sendSocketList.end (); iter++ ) + { + NS_LOG_INFO ("Checking socket for interface " << interface); + if (iter->second == interface) + { + NS_LOG_INFO ("Removed socket for interface " << interface); + iter->first->Close (); + m_sendSocketList.erase (iter); + break; + } + } + + if (m_interfaceExclusions.find (interface) == m_interfaceExclusions.end ()) + { + SendTriggeredRouteUpdate (); + } +} + +void Rip::NotifyAddAddress (uint32_t interface, Ipv4InterfaceAddress address) +{ + NS_LOG_FUNCTION (this << interface << address); + + if (!m_ipv4->IsUp (interface)) + { + return; + } + + if (m_interfaceExclusions.find (interface) != m_interfaceExclusions.end ()) + { + return; + } + + Ipv4Address networkAddress = address.GetLocal ().CombineMask (address.GetMask ()); + Ipv4Mask networkMask = address.GetMask (); + + if (address.GetScope () == Ipv4InterfaceAddress::GLOBAL) + { + AddNetworkRouteTo (networkAddress, networkMask, interface); + } + + SendTriggeredRouteUpdate (); +} + +void Rip::NotifyRemoveAddress (uint32_t interface, Ipv4InterfaceAddress address) +{ + NS_LOG_FUNCTION (this << interface << address); + + if (!m_ipv4->IsUp (interface)) + { + return; + } + + if (address.GetScope() != Ipv4InterfaceAddress::GLOBAL) + { + return; + } + + Ipv4Address networkAddress = address.GetLocal ().CombineMask (address.GetMask ()); + Ipv4Mask networkMask = address.GetMask (); + + // Remove all routes that are going through this interface + // which reference this network + for (RoutesI it = m_routes.begin (); it != m_routes.end (); it++) + { + if (it->first->GetInterface () == interface + && it->first->IsNetwork () + && it->first->GetDestNetwork () == networkAddress + && it->first->GetDestNetworkMask () == networkMask) + { + InvalidateRoute (it->first); + } + } + + if (m_interfaceExclusions.find (interface) == m_interfaceExclusions.end ()) + { + SendTriggeredRouteUpdate (); + } + +} + +void Rip::SetIpv4 (Ptr ipv4) +{ + NS_LOG_FUNCTION (this << ipv4); + + NS_ASSERT (m_ipv4 == 0 && ipv4 != 0); + uint32_t i = 0; + m_ipv4 = ipv4; + + for (i = 0; i < m_ipv4->GetNInterfaces (); i++) + { + if (m_ipv4->IsUp (i)) + { + NotifyInterfaceUp (i); + } + else + { + NotifyInterfaceDown (i); + } + } +} + +void Rip::PrintRoutingTable (Ptr stream) const +{ + NS_LOG_FUNCTION (this << stream); + + std::ostream* os = stream->GetStream (); + + *os << "Node: " << m_ipv4->GetObject ()->GetId () + << ", Time: " << Now().As (Time::S) + << ", Local time: " << GetObject ()->GetLocalTime ().As (Time::S) + << ", IPv4 RIP table" << std::endl; + + if (!m_routes.empty ()) + { + *os << "Destination Gateway Genmask Flags Metric Ref Use Iface" << std::endl; + for (RoutesCI it = m_routes.begin (); it != m_routes.end (); it++) + { + RipRoutingTableEntry* route = it->first; + RipRoutingTableEntry::Status_e status = route->GetRouteStatus(); + + if (status == RipRoutingTableEntry::RIP_VALID) + { + std::ostringstream dest, gw, mask, flags; + dest << route->GetDest (); + *os << std::setiosflags (std::ios::left) << std::setw (16) << dest.str (); + gw << route->GetGateway (); + *os << std::setiosflags (std::ios::left) << std::setw (16) << gw.str (); + mask << route->GetDestNetworkMask (); + *os << std::setiosflags (std::ios::left) << std::setw (16) << mask.str (); + flags << "U"; + if (route->IsHost ()) + { + flags << "HS"; + } + else if (route->IsGateway ()) + { + flags << "GS"; + } + *os << std::setiosflags (std::ios::left) << std::setw (6) << flags.str (); + *os << std::setiosflags (std::ios::left) << std::setw (7) << int(route->GetRouteMetric ()); + // Ref ct not implemented + *os << "-" << " "; + // Use not implemented + *os << "-" << " "; + if (Names::FindName (m_ipv4->GetNetDevice (route->GetInterface ())) != "") + { + *os << Names::FindName (m_ipv4->GetNetDevice (route->GetInterface ())); + } + else + { + *os << route->GetInterface (); + } + *os << std::endl; + } + } + } + *os << std::endl; +} + +void Rip::DoDispose () +{ + NS_LOG_FUNCTION (this); + + for (RoutesI j = m_routes.begin (); j != m_routes.end (); j = m_routes.erase (j)) + { + delete j->first; + } + m_routes.clear (); + + m_nextTriggeredUpdate.Cancel (); + m_nextUnsolicitedUpdate.Cancel (); + m_nextTriggeredUpdate = EventId (); + m_nextUnsolicitedUpdate = EventId (); + + for (SocketListI iter = m_sendSocketList.begin (); iter != m_sendSocketList.end (); iter++ ) + { + iter->first->Close (); + } + m_sendSocketList.clear (); + + m_recvSocket->Close (); + m_recvSocket = 0; + + m_ipv4 = 0; + + Ipv4RoutingProtocol::DoDispose (); +} + + +Ptr Rip::Lookup (Ipv4Address dst, Ptr interface) +{ + NS_LOG_FUNCTION (this << dst << interface); + + Ptr rtentry = 0; + uint16_t longestMask = 0; + + /* when sending on local multicast, there have to be interface specified */ + if (dst.IsLocalMulticast ()) + { + NS_ASSERT_MSG (interface, "Try to send on local multicast address, and no interface index is given!"); + rtentry = Create (); + rtentry->SetSource (m_ipv4->SourceAddressSelection (m_ipv4->GetInterfaceForDevice (interface), dst)); + rtentry->SetDestination (dst); + rtentry->SetGateway (Ipv4Address::GetZero ()); + rtentry->SetOutputDevice (interface); + return rtentry; + } + + for (RoutesI it = m_routes.begin (); it != m_routes.end (); it++) + { + RipRoutingTableEntry* j = it->first; + + if (j->GetRouteStatus () == RipRoutingTableEntry::RIP_VALID) + { + Ipv4Mask mask = j->GetDestNetworkMask (); + uint16_t maskLen = mask.GetPrefixLength (); + Ipv4Address entry = j->GetDestNetwork (); + + NS_LOG_LOGIC ("Searching for route to " << dst << ", mask length " << maskLen); + + if (mask.IsMatch (dst, entry)) + { + NS_LOG_LOGIC ("Found global network route " << j << ", mask length " << maskLen); + + /* if interface is given, check the route will output on this interface */ + if (!interface || interface == m_ipv4->GetNetDevice (j->GetInterface ())) + { + if (maskLen < longestMask) + { + NS_LOG_LOGIC ("Previous match longer, skipping"); + continue; + } + + longestMask = maskLen; + + Ipv4RoutingTableEntry* route = j; + uint32_t interfaceIdx = route->GetInterface (); + rtentry = Create (); + + if (route->GetDest ().IsAny ()) /* default route */ + { + rtentry->SetSource (m_ipv4->SourceAddressSelection (interfaceIdx, route->GetGateway ())); + } + else + { + rtentry->SetSource (m_ipv4->SourceAddressSelection (interfaceIdx, route->GetDest ())); + } + + rtentry->SetDestination (route->GetDest ()); + rtentry->SetGateway (route->GetGateway ()); + rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIdx)); + } + } + } + } + + if (rtentry) + { + NS_LOG_LOGIC ("Matching route via " << rtentry->GetDestination () << " (through " << rtentry->GetGateway () << ") at the end"); + } + return rtentry; +} + +void Rip::AddNetworkRouteTo (Ipv4Address network, Ipv4Mask networkPrefix, Ipv4Address nextHop, uint32_t interface) +{ + NS_LOG_FUNCTION (this << network << networkPrefix << nextHop << interface); + + RipRoutingTableEntry* route = new RipRoutingTableEntry (network, networkPrefix, nextHop, interface); + route->SetRouteMetric (1); + route->SetRouteStatus (RipRoutingTableEntry::RIP_VALID); + route->SetRouteChanged (true); + + m_routes.push_back (std::make_pair (route, EventId ())); +} + +void Rip::AddNetworkRouteTo (Ipv4Address network, Ipv4Mask networkPrefix, uint32_t interface) +{ + NS_LOG_FUNCTION (this << network << networkPrefix << interface); + + RipRoutingTableEntry* route = new RipRoutingTableEntry (network, networkPrefix, interface); + route->SetRouteMetric (1); + route->SetRouteStatus (RipRoutingTableEntry::RIP_VALID); + route->SetRouteChanged (true); + + m_routes.push_back (std::make_pair (route, EventId ())); +} + +void Rip::InvalidateRoute (RipRoutingTableEntry *route) +{ + NS_LOG_FUNCTION (this << *route); + + for (RoutesI it = m_routes.begin (); it != m_routes.end (); it++) + { + if (it->first == route) + { + route->SetRouteStatus (RipRoutingTableEntry::RIP_INVALID); + route->SetRouteMetric (m_linkDown); + route->SetRouteChanged (true); + if (it->second.IsRunning ()) + { + it->second.Cancel (); + } + it->second = Simulator::Schedule (m_garbageCollectionDelay, &Rip::DeleteRoute, this, route); + return; + } + } + NS_ABORT_MSG ("RIP::InvalidateRoute - cannot find the route to update"); +} + +void Rip::DeleteRoute (RipRoutingTableEntry *route) +{ + NS_LOG_FUNCTION (this << *route); + + for (RoutesI it = m_routes.begin (); it != m_routes.end (); it++) + { + if (it->first == route) + { + delete route; + m_routes.erase (it); + return; + } + } + NS_ABORT_MSG ("RIP::DeleteRoute - cannot find the route to delete"); +} + + +void Rip::Receive (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + + Ptr packet = socket->Recv (); + NS_LOG_INFO ("Received " << *packet); + + Ipv4PacketInfoTag interfaceInfo; + if (!packet->RemovePacketTag (interfaceInfo)) + { + NS_ABORT_MSG ("No incoming interface on RIP message, aborting."); + } + uint32_t incomingIf = interfaceInfo.GetRecvIf (); + Ptr node = this->GetObject (); + Ptr dev = node->GetDevice (incomingIf); + uint32_t ipInterfaceIndex = m_ipv4->GetInterfaceForDevice (dev); + + SocketIpTtlTag hoplimitTag; + if (!packet->RemovePacketTag (hoplimitTag)) + { + NS_ABORT_MSG ("No incoming Hop Count on RIP message, aborting."); + } + uint8_t hopLimit = hoplimitTag.GetTtl (); + + SocketAddressTag tag; + if (!packet->RemovePacketTag (tag)) + { + NS_ABORT_MSG ("No incoming sender address on RIP message, aborting."); + } + Ipv4Address senderAddress = InetSocketAddress::ConvertFrom (tag.GetAddress ()).GetIpv4 (); + uint16_t senderPort = InetSocketAddress::ConvertFrom (tag.GetAddress ()).GetPort (); + + int32_t interfaceForAddress = m_ipv4->GetInterfaceForAddress (senderAddress); + if (interfaceForAddress != -1) + { + NS_LOG_LOGIC ("Ignoring a packet sent by myself."); + return; + } + + RipHeader hdr; + packet->RemoveHeader (hdr); + + if (hdr.GetCommand () == RipHeader::RESPONSE) + { + HandleResponses (hdr, senderAddress, ipInterfaceIndex, hopLimit); + } + else if (hdr.GetCommand () == RipHeader::REQUEST) + { + HandleRequests (hdr, senderAddress, senderPort, ipInterfaceIndex, hopLimit); + } + else + { + NS_LOG_LOGIC ("Ignoring message with unknown command: " << int (hdr.GetCommand ())); + } + return; +} + +void Rip::HandleRequests (RipHeader requestHdr, Ipv4Address senderAddress, uint16_t senderPort, uint32_t incomingInterface, uint8_t hopLimit) +{ + NS_LOG_FUNCTION (this << senderAddress << int (senderPort) << incomingInterface << int (hopLimit) << requestHdr); + + std::list rtes = requestHdr.GetRteList (); + + if (rtes.empty ()) + { + return; + } + + // check if it's a request for the full table from a neighbor + if (rtes.size () == 1) + { + if (rtes.begin ()->GetPrefix () == Ipv4Address::GetAny () && + rtes.begin ()->GetSubnetMask ().GetPrefixLength () == 0 && + rtes.begin ()->GetRouteMetric () == m_linkDown) + { + // Output whole thing. Use Split Horizon + if (m_interfaceExclusions.find (incomingInterface) == m_interfaceExclusions.end ()) + { + // we use one of the sending sockets, as they're bound to the right interface + // and the local address might be used on different interfaces. + Ptr sendingSoket; + for (SocketListI iter = m_sendSocketList.begin (); iter != m_sendSocketList.end (); iter++ ) + { + if (iter->second == incomingInterface) + { + sendingSoket = iter->first; + } + } + NS_ASSERT_MSG (sendingSoket, "HandleRequest - Impossible to find a socket to send the reply"); + + uint16_t mtu = m_ipv4->GetMtu (incomingInterface); + uint16_t maxRte = (mtu - Ipv4Header ().GetSerializedSize () - UdpHeader ().GetSerializedSize () - RipHeader ().GetSerializedSize ()) / RipRte ().GetSerializedSize (); + + Ptr p = Create (); + SocketIpTtlTag tag; + p->RemovePacketTag (tag); + if (senderAddress == Ipv4Address(RIP_ALL_NODE)) + { + tag.SetTtl (1); + } + else + { + tag.SetTtl (255); + } + p->AddPacketTag (tag); + + RipHeader hdr; + hdr.SetCommand (RipHeader::RESPONSE); + + for (RoutesI rtIter = m_routes.begin (); rtIter != m_routes.end (); rtIter++) + { + bool splitHorizoning = (rtIter->first->GetInterface () == incomingInterface); + + Ipv4InterfaceAddress rtDestAddr = Ipv4InterfaceAddress(rtIter->first->GetDestNetwork (), rtIter->first->GetDestNetworkMask ()); + + bool isGlobal = (rtDestAddr.GetScope () == Ipv4InterfaceAddress::GLOBAL); + bool isDefaultRoute = ((rtIter->first->GetDestNetwork () == Ipv4Address::GetAny ()) && + (rtIter->first->GetDestNetworkMask () == Ipv4Mask::GetZero ()) && + (rtIter->first->GetInterface () != incomingInterface)); + + if ((isGlobal || isDefaultRoute) && + (rtIter->first->GetRouteStatus () == RipRoutingTableEntry::RIP_VALID) ) + { + RipRte rte; + rte.SetPrefix (rtIter->first->GetDestNetwork ()); + rte.SetSubnetMask (rtIter->first->GetDestNetworkMask ()); + if (m_splitHorizonStrategy == POISON_REVERSE && splitHorizoning) + { + rte.SetRouteMetric (m_linkDown); + } + else + { + rte.SetRouteMetric (rtIter->first->GetRouteMetric ()); + } + rte.SetRouteTag (rtIter->first->GetRouteTag ()); + if ((m_splitHorizonStrategy != SPLIT_HORIZON) || + (m_splitHorizonStrategy == SPLIT_HORIZON && !splitHorizoning)) + { + hdr.AddRte (rte); + } + } + if (hdr.GetRteNumber () == maxRte) + { + p->AddHeader (hdr); + NS_LOG_DEBUG ("SendTo: " << *p); + sendingSoket->SendTo (p, 0, InetSocketAddress (senderAddress, RIP_PORT)); + p->RemoveHeader (hdr); + hdr.ClearRtes (); + } + } + if (hdr.GetRteNumber () > 0) + { + p->AddHeader (hdr); + NS_LOG_DEBUG ("SendTo: " << *p); + sendingSoket->SendTo (p, 0, InetSocketAddress (senderAddress, RIP_PORT)); + } + } + } + } + else + { + // note: we got the request as a single packet, so no check is necessary for MTU limit + + Ptr p = Create (); + SocketIpTtlTag tag; + p->RemovePacketTag (tag); + if (senderAddress == Ipv4Address(RIP_ALL_NODE)) + { + tag.SetTtl (1); + } + else + { + tag.SetTtl (255); + } + p->AddPacketTag (tag); + + RipHeader hdr; + hdr.SetCommand (RipHeader::RESPONSE); + + for (std::list::iterator iter = rtes.begin (); + iter != rtes.end (); iter++) + { + bool found = false; + for (RoutesI rtIter = m_routes.begin (); rtIter != m_routes.end (); rtIter++) + { + + Ipv4InterfaceAddress rtDestAddr = Ipv4InterfaceAddress (rtIter->first->GetDestNetwork (), rtIter->first->GetDestNetworkMask ()); + if ((rtDestAddr.GetScope () == Ipv4InterfaceAddress::GLOBAL) && + (rtIter->first->GetRouteStatus () == RipRoutingTableEntry::RIP_VALID)) + { + Ipv4Address requestedAddress = iter->GetPrefix (); + requestedAddress.CombineMask (iter->GetSubnetMask ()); + Ipv4Address rtAddress = rtIter->first->GetDestNetwork (); + rtAddress.CombineMask (rtIter->first->GetDestNetworkMask ()); + + if (requestedAddress == rtAddress) + { + iter->SetRouteMetric (rtIter->first->GetRouteMetric ()); + iter->SetRouteTag (rtIter->first->GetRouteTag ()); + hdr.AddRte (*iter); + found = true; + break; + } + } + } + if (!found) + { + iter->SetRouteMetric (m_linkDown); + iter->SetRouteTag (0); + hdr.AddRte (*iter); + } + } + p->AddHeader (hdr); + NS_LOG_DEBUG ("SendTo: " << *p); + m_recvSocket->SendTo (p, 0, InetSocketAddress (senderAddress, senderPort)); + } + +} + +void Rip::HandleResponses (RipHeader hdr, Ipv4Address senderAddress, uint32_t incomingInterface, uint8_t hopLimit) +{ + NS_LOG_FUNCTION (this << senderAddress << incomingInterface << int (hopLimit) << hdr); + + if (m_interfaceExclusions.find (incomingInterface) != m_interfaceExclusions.end ()) + { + NS_LOG_LOGIC ("Ignoring an update message from an excluded interface: " << incomingInterface); + return; + } + + std::list rtes = hdr.GetRteList (); + + // validate the RTEs before processing + for (std::list::iterator iter = rtes.begin (); + iter != rtes.end (); iter++) + { + if (iter->GetRouteMetric () == 0 || iter->GetRouteMetric () > m_linkDown) + { + NS_LOG_LOGIC ("Ignoring an update message with malformed metric: " << int (iter->GetRouteMetric ())); + return; + } + if (iter->GetPrefix ().IsLocalhost () || + iter->GetPrefix ().IsBroadcast () || + iter->GetPrefix ().IsMulticast ()) + { + NS_LOG_LOGIC ("Ignoring an update message with wrong prefixes: " << iter->GetPrefix ()); + return; + } + } + + bool changed = false; + + for (std::list::iterator iter = rtes.begin (); + iter != rtes.end (); iter++) + { + Ipv4Mask rtePrefixMask = iter->GetSubnetMask (); + Ipv4Address rteAddr = iter->GetPrefix ().CombineMask (rtePrefixMask); + + NS_LOG_LOGIC ("Processing RTE " << *iter); + + uint32_t interfaceMetric = 1; + if (m_interfaceMetrics.find (incomingInterface) != m_interfaceMetrics.end ()) + { + interfaceMetric = m_interfaceMetrics[incomingInterface]; + } + uint64_t rteMetric = iter->GetRouteMetric () + interfaceMetric; + if (rteMetric > m_linkDown) + { + rteMetric = m_linkDown; + } + + RoutesI it; + bool found = false; + for (it = m_routes.begin (); it != m_routes.end (); it++) + { + if (it->first->GetDestNetwork () == rteAddr && + it->first->GetDestNetworkMask () == rtePrefixMask) + { + found = true; + if (rteMetric < it->first->GetRouteMetric ()) + { + if (senderAddress != it->first->GetGateway ()) + { + RipRoutingTableEntry* route = new RipRoutingTableEntry (rteAddr, rtePrefixMask, senderAddress, incomingInterface); + delete it->first; + it->first = route; + } + it->first->SetRouteMetric (rteMetric); + it->first->SetRouteStatus (RipRoutingTableEntry::RIP_VALID); + it->first->SetRouteTag (iter->GetRouteTag ()); + it->first->SetRouteChanged (true); + it->second.Cancel (); + it->second = Simulator::Schedule (m_timeoutDelay, &Rip::InvalidateRoute, this, it->first); + changed = true; + } + else if (rteMetric == it->first->GetRouteMetric ()) + { + if (senderAddress == it->first->GetGateway ()) + { + it->second.Cancel (); + it->second = Simulator::Schedule (m_timeoutDelay, &Rip::InvalidateRoute, this, it->first); + } + else + { + if (Simulator::GetDelayLeft (it->second) < m_timeoutDelay/2) + { + RipRoutingTableEntry* route = new RipRoutingTableEntry (rteAddr, rtePrefixMask, senderAddress, incomingInterface); + route->SetRouteMetric (rteMetric); + route->SetRouteStatus (RipRoutingTableEntry::RIP_VALID); + route->SetRouteTag (iter->GetRouteTag ()); + route->SetRouteChanged (true); + delete it->first; + it->first = route; + it->second.Cancel (); + it->second = Simulator::Schedule (m_timeoutDelay, &Rip::InvalidateRoute, this, route); + changed = true; + } + } + } + else if (rteMetric > it->first->GetRouteMetric () && senderAddress == it->first->GetGateway ()) + { + it->second.Cancel (); + if (rteMetric < m_linkDown) + { + it->first->SetRouteMetric (rteMetric); + it->first->SetRouteStatus (RipRoutingTableEntry::RIP_VALID); + it->first->SetRouteTag (iter->GetRouteTag ()); + it->first->SetRouteChanged (true); + it->second.Cancel (); + it->second = Simulator::Schedule (m_timeoutDelay, &Rip::InvalidateRoute, this, it->first); + } + else + { + InvalidateRoute (it->first); + } + changed = true; + } + } + } + if (!found && rteMetric != m_linkDown) + { + NS_LOG_LOGIC ("Received a RTE with new route, adding."); + + RipRoutingTableEntry* route = new RipRoutingTableEntry (rteAddr, rtePrefixMask, senderAddress, incomingInterface); + route->SetRouteMetric (rteMetric); + route->SetRouteStatus (RipRoutingTableEntry::RIP_VALID); + route->SetRouteChanged (true); + m_routes.push_front (std::make_pair (route, EventId ())); + EventId invalidateEvent = Simulator::Schedule (m_timeoutDelay, &Rip::InvalidateRoute, this, route); + (m_routes.begin ())->second = invalidateEvent; + changed = true; + } + } + + if (changed) + { + SendTriggeredRouteUpdate (); + } +} + +void Rip::DoSendRouteUpdate (bool periodic) +{ + NS_LOG_FUNCTION (this << (periodic ? " periodic" : " triggered")); + + for (SocketListI iter = m_sendSocketList.begin (); iter != m_sendSocketList.end (); iter++ ) + { + uint32_t interface = iter->second; + + if (m_interfaceExclusions.find (interface) == m_interfaceExclusions.end ()) + { + uint16_t mtu = m_ipv4->GetMtu (interface); + uint16_t maxRte = (mtu - Ipv4Header ().GetSerializedSize () - UdpHeader ().GetSerializedSize () - RipHeader ().GetSerializedSize ()) / RipRte ().GetSerializedSize (); + + Ptr p = Create (); + SocketIpTtlTag tag; + tag.SetTtl (1); + p->AddPacketTag (tag); + + RipHeader hdr; + hdr.SetCommand (RipHeader::RESPONSE); + + for (RoutesI rtIter = m_routes.begin (); rtIter != m_routes.end (); rtIter++) + { + bool splitHorizoning = (rtIter->first->GetInterface () == interface); + Ipv4InterfaceAddress rtDestAddr = Ipv4InterfaceAddress(rtIter->first->GetDestNetwork (), rtIter->first->GetDestNetworkMask ()); + + NS_LOG_DEBUG ("Processing RT " << rtDestAddr << " " << int(rtIter->first->IsRouteChanged ())); + + bool isGlobal = (rtDestAddr.GetScope () == Ipv4InterfaceAddress::GLOBAL); + bool isDefaultRoute = ((rtIter->first->GetDestNetwork () == Ipv4Address::GetAny ()) && + (rtIter->first->GetDestNetworkMask () == Ipv4Mask::GetZero ()) && + (rtIter->first->GetInterface () != interface)); + + bool sameNetwork = false; + for (uint32_t index = 0; index < m_ipv4->GetNAddresses (interface); index++) + { + Ipv4InterfaceAddress addr = m_ipv4->GetAddress (interface, index); + if (addr.GetLocal ().CombineMask (addr.GetMask ()) == rtIter->first->GetDestNetwork ()) + { + sameNetwork = true; + } + } + + if ((isGlobal || isDefaultRoute) && + (periodic || rtIter->first->IsRouteChanged ()) && + !sameNetwork) + { + RipRte rte; + rte.SetPrefix (rtIter->first->GetDestNetwork ()); + rte.SetSubnetMask (rtIter->first->GetDestNetworkMask ()); + if (m_splitHorizonStrategy == POISON_REVERSE && splitHorizoning) + { + rte.SetRouteMetric (m_linkDown); + } + else + { + rte.SetRouteMetric (rtIter->first->GetRouteMetric ()); + } + rte.SetRouteTag (rtIter->first->GetRouteTag ()); + if (m_splitHorizonStrategy == SPLIT_HORIZON && !splitHorizoning) + { + hdr.AddRte (rte); + } + else if (m_splitHorizonStrategy != SPLIT_HORIZON) + { + hdr.AddRte (rte); + } + } + if (hdr.GetRteNumber () == maxRte) + { + p->AddHeader (hdr); + NS_LOG_DEBUG ("SendTo: " << *p); + iter->first->SendTo (p, 0, InetSocketAddress (RIP_ALL_NODE, RIP_PORT)); + p->RemoveHeader (hdr); + hdr.ClearRtes (); + } + } + if (hdr.GetRteNumber () > 0) + { + p->AddHeader (hdr); + NS_LOG_DEBUG ("SendTo: " << *p); + iter->first->SendTo (p, 0, InetSocketAddress (RIP_ALL_NODE, RIP_PORT)); + } + } + } + for (RoutesI rtIter = m_routes.begin (); rtIter != m_routes.end (); rtIter++) + { + rtIter->first->SetRouteChanged (false); + } +} + +void Rip::SendTriggeredRouteUpdate () +{ + NS_LOG_FUNCTION (this); + + if (m_nextTriggeredUpdate.IsRunning()) + { + NS_LOG_LOGIC ("Skipping Triggered Update due to cooldown"); + return; + } + + // DoSendRouteUpdate (false); + + // note: The RFC states: + // After a triggered + // update is sent, a timer should be set for a random interval between 1 + // and 5 seconds. If other changes that would trigger updates occur + // before the timer expires, a single update is triggered when the timer + // expires. The timer is then reset to another random value between 1 + // and 5 seconds. Triggered updates may be suppressed if a regular + // update is due by the time the triggered update would be sent. + // Here we rely on this: + // When an update occurs (either Triggered or Periodic) the "IsChanged ()" + // route field will be cleared. + // Hence, the following Triggered Update will be fired, but will not send + // any route update. + + Time delay = Seconds (m_rng->GetValue (m_minTriggeredUpdateDelay.GetSeconds (), m_maxTriggeredUpdateDelay.GetSeconds ())); + m_nextTriggeredUpdate = Simulator::Schedule (delay, &Rip::DoSendRouteUpdate, this, false); +} + +void Rip::SendUnsolicitedRouteUpdate () +{ + NS_LOG_FUNCTION (this); + + if (m_nextTriggeredUpdate.IsRunning()) + { + m_nextTriggeredUpdate.Cancel (); + } + + DoSendRouteUpdate (true); + + Time delay = m_unsolicitedUpdate + Seconds (m_rng->GetValue (0, 0.5*m_unsolicitedUpdate.GetSeconds ()) ); + m_nextUnsolicitedUpdate = Simulator::Schedule (delay, &Rip::SendUnsolicitedRouteUpdate, this); +} + +std::set Rip::GetInterfaceExclusions () const +{ + return m_interfaceExclusions; +} + +void Rip::SetInterfaceExclusions (std::set exceptions) +{ + NS_LOG_FUNCTION (this); + + m_interfaceExclusions = exceptions; +} + +uint8_t Rip::GetInterfaceMetric (uint32_t interface) const +{ + NS_LOG_FUNCTION (this << interface); + + std::map::const_iterator iter = m_interfaceMetrics.find (interface); + if (iter != m_interfaceMetrics.end ()) + { + return iter->second; + } + return 1; +} + +void Rip::SetInterfaceMetric (uint32_t interface, uint8_t metric) +{ + NS_LOG_FUNCTION (this << interface << int (metric)); + + if (metric < m_linkDown) + { + m_interfaceMetrics[interface] = metric; + } +} + +void Rip::SendRouteRequest () +{ + NS_LOG_FUNCTION (this); + + Ptr p = Create (); + SocketIpTtlTag tag; + p->RemovePacketTag (tag); + tag.SetTtl (1); + p->AddPacketTag (tag); + + RipHeader hdr; + hdr.SetCommand (RipHeader::REQUEST); + + RipRte rte; + rte.SetPrefix (Ipv4Address::GetAny ()); + rte.SetSubnetMask (Ipv4Mask::GetZero ()); + rte.SetRouteMetric (m_linkDown); + + hdr.AddRte (rte); + p->AddHeader (hdr); + + for (SocketListI iter = m_sendSocketList.begin (); iter != m_sendSocketList.end (); iter++ ) + { + uint32_t interface = iter->second; + + if (m_interfaceExclusions.find (interface) == m_interfaceExclusions.end ()) + { + NS_LOG_DEBUG ("SendTo: " << *p); + iter->first->SendTo (p, 0, InetSocketAddress (RIP_ALL_NODE, RIP_PORT)); + } + } +} + +void Rip::AddDefaultRouteTo (Ipv4Address nextHop, uint32_t interface) +{ + NS_LOG_FUNCTION (this << interface); + + AddNetworkRouteTo (Ipv4Address ("0.0.0.0"), Ipv4Mask::GetZero (), nextHop, interface); +} + + +/* + * RipRoutingTableEntry + */ + +RipRoutingTableEntry::RipRoutingTableEntry () + : m_tag (0), m_metric (0), m_status (RIP_INVALID), m_changed (false) +{ +} + +RipRoutingTableEntry::RipRoutingTableEntry (Ipv4Address network, Ipv4Mask networkPrefix, Ipv4Address nextHop, uint32_t interface) + : Ipv4RoutingTableEntry ( Ipv4RoutingTableEntry::CreateNetworkRouteTo (network, networkPrefix, nextHop, interface) ), + m_tag (0), m_metric (0), m_status (RIP_INVALID), m_changed (false) +{ +} + +RipRoutingTableEntry::RipRoutingTableEntry (Ipv4Address network, Ipv4Mask networkPrefix, uint32_t interface) + : Ipv4RoutingTableEntry ( Ipv4RoutingTableEntry::CreateNetworkRouteTo (network, networkPrefix, interface) ), + m_tag (0), m_metric (0), m_status (RIP_INVALID), m_changed (false) +{ +} + +RipRoutingTableEntry::~RipRoutingTableEntry () +{ +} + + +void RipRoutingTableEntry::SetRouteTag (uint16_t routeTag) +{ + if (m_tag != routeTag) + { + m_tag = routeTag; + m_changed = true; + } +} + +uint16_t RipRoutingTableEntry::GetRouteTag () const +{ + return m_tag; +} + +void RipRoutingTableEntry::SetRouteMetric (uint8_t routeMetric) +{ + if (m_metric != routeMetric) + { + m_metric = routeMetric; + m_changed = true; + } +} + +uint8_t RipRoutingTableEntry::GetRouteMetric () const +{ + return m_metric; +} + +void RipRoutingTableEntry::SetRouteStatus (Status_e status) +{ + if (m_status != status) + { + m_status = status; + m_changed = true; + } +} + +RipRoutingTableEntry::Status_e RipRoutingTableEntry::GetRouteStatus (void) const +{ + return m_status; +} + +void RipRoutingTableEntry::SetRouteChanged (bool changed) +{ + m_changed = changed; +} + +bool RipRoutingTableEntry::IsRouteChanged (void) const +{ + return m_changed; +} + + +std::ostream & operator << (std::ostream& os, const RipRoutingTableEntry& rte) +{ + os << static_cast(rte); + os << ", metric: " << int (rte.GetRouteMetric ()) << ", tag: " << int (rte.GetRouteTag ()); + + return os; +} + + +} + diff --git a/src/internet/model/rip.h b/src/internet/model/rip.h new file mode 100644 index 000000000..2810a2ad5 --- /dev/null +++ b/src/internet/model/rip.h @@ -0,0 +1,409 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#ifndef RIP_H +#define RIP_H + +#include + +#include "ns3/ipv4-routing-protocol.h" +#include "ns3/ipv4-interface.h" +#include "ns3/inet-socket-address.h" +#include "ns3/ipv4-l3-protocol.h" +#include "ns3/ipv4-routing-table-entry.h" +#include "ns3/random-variable-stream.h" +#include "ns3/rip-header.h" + +namespace ns3 { + +/** + * \defgroup rip RIP + * + * The RIP protocol (\RFC{2453}) is a unicast-only IPv4 IGP (Interior Gateway Protocol). + * Its convergence time is rather long. As a consequence, it is suggested to + * carefully check the network topology and the route status before sending + * data flows. + * + * RIP implements the Bellman-Ford algorithm (although the RFC does not state it). + * Bellman-Ford algorithm convergence time is O(|V|*|E|) where |V| and |E| are the + * number of vertices (routers) and edges (links) respectively. Since unsolicited + * updates are exchanged every 30 seconds, the convergence might require a long time. + * + * For the RIP protocol, the exact convergence time is shorter, thanks to the + * use of triggered updates, which are sent when a route changes. + * Even with triggered updates, the convergence is in the order of magnitude of + * O(|V|*|E|) * 5 seconds, which is still quite long for complex topologies. + * + * \todo: Add routing table compression (CIDR). The most evident result: without + * it a router will announce to be the default router *and* more RTEs, which is silly. + */ + +/** + * \ingroup rip + * \brief Rip Routing Table Entry + */ +class RipRoutingTableEntry : public Ipv4RoutingTableEntry +{ +public: + + /** + * Route status + */ + enum Status_e { + RIP_VALID, + RIP_INVALID, + }; + + RipRoutingTableEntry (void); + + /** + * \brief Constructor + * \param network network address + * \param networkPrefix network prefix + * \param nextHop next hop address to route the packet + * \param interface interface index + */ + RipRoutingTableEntry (Ipv4Address network, Ipv4Mask networkPrefix, Ipv4Address nextHop, uint32_t interface); + + /** + * \brief Constructor + * \param network network address + * \param networkPrefix network prefix + * \param interface interface index + */ + RipRoutingTableEntry (Ipv4Address network, Ipv4Mask networkPrefix, uint32_t interface); + + virtual ~RipRoutingTableEntry (); + + /** + * \brief Set the route tag + * \param routeTag the route tag + */ + void SetRouteTag (uint16_t routeTag); + + /** + * \brief Get the route tag + * \returns the route tag + */ + uint16_t GetRouteTag (void) const; + + /** + * \brief Set the route metric + * \param routeMetric the route metric + */ + void SetRouteMetric (uint8_t routeMetric); + + /** + * \brief Get the route metric + * \returns the route metric + */ + uint8_t GetRouteMetric (void) const; + + /** + * \brief Set the route status + * \param status the route status + */ + void SetRouteStatus (Status_e status); + + /** + * \brief Get the route status + * \returns the route status + */ + Status_e GetRouteStatus (void) const; + + /** + * \brief Set the route as changed + * + * The changed routes are scheduled for a Triggered Update. + * After a Triggered Update, all the changed flags are cleared + * from the routing table. + * + * \param changed true if route is changed + */ + void SetRouteChanged (bool changed); + + /** + * \brief Get the route changed status + * + * \returns true if route is changed + */ + bool IsRouteChanged (void) const; + +private: + uint16_t m_tag; //!< route tag + uint8_t m_metric; //!< route metric + Status_e m_status; //!< route status + bool m_changed; //!< route has been updated +}; + +/** + * \brief Stream insertion operator. + * + * \param os the reference to the output stream + * \param route the Ipv4 routing table entry + * \returns the reference to the output stream + */ +std::ostream& operator<< (std::ostream& os, RipRoutingTableEntry const& route); + + + +/** + * \ingroup rip + * + * \brief RIP Routing Protocol, defined in \RFC{2453}. + */ +class Rip : public Ipv4RoutingProtocol +{ +public: + // /< C-tor + Rip (); + virtual ~Rip (); + + /** + * \brief Get the type ID + * \return type ID + */ + static TypeId GetTypeId (void); + + // From Ipv4RoutingProtocol + Ptr RouteOutput (Ptr p, const Ipv4Header &header, Ptr oif, + Socket::SocketErrno &sockerr); + bool RouteInput (Ptr p, const Ipv4Header &header, Ptr idev, + UnicastForwardCallback ucb, MulticastForwardCallback mcb, + LocalDeliverCallback lcb, ErrorCallback ecb); + virtual void NotifyInterfaceUp (uint32_t interface); + virtual void NotifyInterfaceDown (uint32_t interface); + virtual void NotifyAddAddress (uint32_t interface, Ipv4InterfaceAddress address); + virtual void NotifyRemoveAddress (uint32_t interface, Ipv4InterfaceAddress address); + virtual void SetIpv4 (Ptr ipv4); + virtual void PrintRoutingTable (Ptr stream) const; + + /** + * Split Horizon strategy type. See \RFC{2453}. + */ + enum SplitHorizonType_e { + NO_SPLIT_HORIZON,//!< No Split Horizon + SPLIT_HORIZON, //!< Split Horizon + POISON_REVERSE, //!< Poison Reverse Split Horizon + }; + + /** + * Assign a fixed random variable stream number to the random variables + * used by this model. Return the number of streams (possibly zero) that + * have been assigned. + * + * \param stream first stream index to use + * \return the number of stream indices assigned by this model + */ + int64_t AssignStreams (int64_t stream); + + /** + * \brief Get the set of interface excluded from the protocol + * \return the set of excluded interfaces + */ + std::set GetInterfaceExclusions () const; + + /** + * \brief Set the set of interface excluded from the protocol + * \param exceptions the set of excluded interfaces + */ + void SetInterfaceExclusions (std::set exceptions); + + /** + * \brief Get the metric for an interface + * \param interface the interface + * \returns the interface metric + */ + uint8_t GetInterfaceMetric (uint32_t interface) const; + + /** + * \brief Set the metric for an interface + * \param interface the interface + * \param metric the interface metric + */ + void SetInterfaceMetric (uint32_t interface, uint8_t metric); + + /** + * \brief Add a default route to the router through the nextHop located on interface. + * + * The default route is usually installed manually, or it is the result of + * some "other" routing protocol (e.g., BGP). + * + * \param nextHop the next hop + * \param interface the interface + */ + void AddDefaultRouteTo (Ipv4Address nextHop, uint32_t interface); + +protected: + /** + * \brief Dispose this object. + */ + virtual void DoDispose (); + + /** + * Start protocol operation + */ + void DoInitialize (); + +private: + /// Container for the network routes - pair RipRoutingTableEntry *, EventId (update event) + typedef std::list > Routes; + + /// Const Iterator for container for the network routes + typedef std::list >::const_iterator RoutesCI; + + /// Iterator for container for the network routes + typedef std::list >::iterator RoutesI; + + + /** + * \brief Receive RIP packets. + * + * \param socket the socket the packet was received to. + */ + void Receive (Ptr socket); + + /** + * \brief Handle RIP requests. + * + * \param hdr message header (including RTEs) + * \param senderAddress sender address + * \param senderPort sender port + * \param incomingInterface incoming interface + * \param hopLimit packet's hop limit + */ + void HandleRequests (RipHeader hdr, Ipv4Address senderAddress, uint16_t senderPort, uint32_t incomingInterface, uint8_t hopLimit); + + /** + * \brief Handle RIP responses. + * + * \param hdr message header (including RTEs) + * \param senderAddress sender address + * \param incomingInterface incoming interface + * \param hopLimit packet's hop limit + */ + void HandleResponses (RipHeader hdr, Ipv4Address senderAddress, uint32_t incomingInterface, uint8_t hopLimit); + + /** + * \brief Lookup in the forwarding table for destination. + * \param dest destination address + * \param interface output interface if any (put 0 otherwise) + * \return Ipv4Route to route the packet to reach dest address + */ + Ptr Lookup (Ipv4Address dest, Ptr = 0); + + /** + * Receive and process unicast packet + * \param socket socket where packet is arrived + */ + void RecvUnicastRip (Ptr socket); + /** + * Receive and process multicast packet + * \param socket socket where packet is arrived + */ + void RecvMulticastRip (Ptr socket); + + /** + * \brief Add route to network. + * \param network network address + * \param networkPrefix network prefix + * \param nextHop next hop address to route the packet. + * \param interface interface index + */ + void AddNetworkRouteTo (Ipv4Address network, Ipv4Mask networkPrefix, Ipv4Address nextHop, uint32_t interface); + + /** + * \brief Add route to network. + * \param network network address + * \param networkPrefix network prefix + * \param interface interface index + */ + void AddNetworkRouteTo (Ipv4Address network, Ipv4Mask networkPrefix, uint32_t interface); + + /** + * \brief Send Routing Updates on all interfaces. + */ + void DoSendRouteUpdate (bool periodic); + + /** + * \brief Send Routing Request on all interfaces. + */ + void SendRouteRequest (); + + /** + * \brief Send Triggered Routing Updates on all interfaces. + */ + void SendTriggeredRouteUpdate (); + + /** + * \brief Send Unsolicited Routing Updates on all interfaces. + */ + void SendUnsolicitedRouteUpdate (void); + + /** + * \brief Invalidate a route. + * \param route the route to be removed + */ + void InvalidateRoute (RipRoutingTableEntry *route); + + /** + * \brief Delete a route. + * \param route the route to be removed + */ + void DeleteRoute (RipRoutingTableEntry *route); + + Routes m_routes; //!< the forwarding table for network. + Ptr m_ipv4; //!< IPv4 reference + Time m_startupDelay; //!< Random delay before protocol startup. + Time m_minTriggeredUpdateDelay; //!< Min cooldown delay after a Triggered Update. + Time m_maxTriggeredUpdateDelay; //!< Max cooldown delay after a Triggered Update. + Time m_unsolicitedUpdate; //!< time between two Unsolicited Routing Updates + Time m_timeoutDelay; //!< Delay before invalidating a route + Time m_garbageCollectionDelay; //!< Delay before deleting an INVALID route + + // note: we can not trust the result of socket->GetBoundNetDevice ()->GetIfIndex (); + // it is dependent on the interface initialization (i.e., if the loopback is already up). + /// Socket list type + typedef std::map< Ptr, uint32_t> SocketList; + /// Socket list type iterator + typedef std::map, uint32_t>::iterator SocketListI; + /// Socket list type const iterator + typedef std::map, uint32_t>::const_iterator SocketListCI; + + SocketList m_sendSocketList; //!< list of sockets for sending (socket, interface index) + Ptr m_recvSocket; //!< receive socket + + EventId m_nextUnsolicitedUpdate; //!< Next Unsolicited Update event + EventId m_nextTriggeredUpdate; //!< Next Triggered Update event + + Ptr m_rng; //!< Rng stream. + + std::set m_interfaceExclusions; //!< Set of excluded interfaces + std::map m_interfaceMetrics; //!< Map of interface metrics + + SplitHorizonType_e m_splitHorizonStrategy; //!< Split Horizon strategy + + bool m_initialized; //!< flag to allow socket's late-creation. + uint32_t m_linkDown; //!< Link down value. +}; + +} // namespace ns3 +#endif /* RIP_H */ + diff --git a/src/internet/test/ipv4-rip-test.cc b/src/internet/test/ipv4-rip-test.cc new file mode 100644 index 000000000..4e8c2bdc8 --- /dev/null +++ b/src/internet/test/ipv4-rip-test.cc @@ -0,0 +1,647 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Universita' di Firenze + * + * 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: Tommaso Pecorella + */ + +#include "ns3/test.h" +#include "ns3/socket-factory.h" +#include "ns3/udp-socket-factory.h" +#include "ns3/simulator.h" +#include "ns3/simple-channel.h" +#include "ns3/simple-net-device.h" +#include "ns3/drop-tail-queue.h" +#include "ns3/socket.h" +#include "ns3/boolean.h" +#include "ns3/enum.h" + +#include "ns3/log.h" +#include "ns3/node.h" +#include "ns3/inet-socket-address.h" + +#include "ns3/internet-stack-helper.h" +#include "ns3/ipv4-address-helper.h" +#include "ns3/ipv4-l3-protocol.h" +#include "ns3/icmpv4-l4-protocol.h" +#include "ns3/udp-l4-protocol.h" +#include "ns3/rip.h" +#include "ns3/rip-helper.h" +#include "ns3/node-container.h" +#include "ns3/ipv4-static-routing.h" + +#include +#include + +using namespace ns3; + +// Ipv4RipTest + +class Ipv4RipTest : public TestCase +{ + Ptr m_receivedPacket; + void DoSendData (Ptr socket, std::string to); + void SendData (Ptr socket, std::string to); + +public: + virtual void DoRun (void); + Ipv4RipTest (); + + void ReceivePkt (Ptr socket); +}; + +Ipv4RipTest::Ipv4RipTest () + : TestCase ("RIP") +{ +} + +void Ipv4RipTest::ReceivePkt (Ptr socket) +{ + uint32_t availableData; + availableData = socket->GetRxAvailable (); + m_receivedPacket = socket->Recv (std::numeric_limits::max (), 0); + NS_ASSERT (availableData == m_receivedPacket->GetSize ()); + //cast availableData to void, to suppress 'availableData' set but not used + //compiler warning + (void) availableData; +} + +void +Ipv4RipTest::DoSendData (Ptr socket, std::string to) +{ + Address realTo = InetSocketAddress (Ipv4Address (to.c_str ()), 1234); + NS_TEST_EXPECT_MSG_EQ (socket->SendTo (Create (123), 0, realTo), + 123, "100"); +} + +void +Ipv4RipTest::SendData (Ptr socket, std::string to) +{ + m_receivedPacket = Create (); + Simulator::ScheduleWithContext (socket->GetNode ()->GetId (), Seconds (60), + &Ipv4RipTest::DoSendData, this, socket, to); + Simulator::Stop (Seconds (66)); + Simulator::Run (); +} + +void +Ipv4RipTest::DoRun (void) +{ + // Create topology + + Ptr txNode = CreateObject (); + Ptr rxNode = CreateObject (); + Ptr routerA = CreateObject (); + Ptr routerB = CreateObject (); + Ptr routerC = CreateObject (); + + NodeContainer nodes (txNode, rxNode); + NodeContainer routers (routerA, routerB, routerC); + NodeContainer all (nodes, routers); + + RipHelper ripRouting; + InternetStackHelper internetRouters; + internetRouters.SetRoutingHelper (ripRouting); + internetRouters.Install (routers); + + InternetStackHelper internetNodes; + internetNodes.Install (nodes); + + NetDeviceContainer net1; + NetDeviceContainer net2; + NetDeviceContainer net3; + NetDeviceContainer net4; + + // Sender Node + Ptr txDev; + { + txDev = CreateObject (); + txDev->SetAddress (Mac48Address ("00:00:00:00:00:01")); + txNode->AddDevice (txDev); + } + net1.Add (txDev); + + // Router A + Ptr fwDev1routerA, fwDev2routerA; + { // first interface + fwDev1routerA = CreateObject (); + fwDev1routerA->SetAddress (Mac48Address ("00:00:00:00:00:02")); + routerA->AddDevice (fwDev1routerA); + } + net1.Add (fwDev1routerA); + + { // second interface + fwDev2routerA = CreateObject (); + fwDev2routerA->SetAddress (Mac48Address ("00:00:00:00:00:03")); + routerA->AddDevice (fwDev2routerA); + } + net2.Add (fwDev2routerA); + + // Router B + Ptr fwDev1routerB, fwDev2routerB; + { // first interface + fwDev1routerB = CreateObject (); + fwDev1routerB->SetAddress (Mac48Address ("00:00:00:00:00:04")); + routerB->AddDevice (fwDev1routerB); + } + net2.Add (fwDev1routerB); + + { // second interface + fwDev2routerB = CreateObject (); + fwDev2routerB->SetAddress (Mac48Address ("00:00:00:00:00:05")); + routerB->AddDevice (fwDev2routerB); + } + net3.Add (fwDev2routerB); + + // Router C + Ptr fwDev1routerC, fwDev2routerC; + { // first interface + fwDev1routerC = CreateObject (); + fwDev1routerC->SetAddress (Mac48Address ("00:00:00:00:00:06")); + routerC->AddDevice (fwDev1routerC); + } + net3.Add (fwDev1routerC); + + { // second interface + fwDev2routerC = CreateObject (); + fwDev2routerC->SetAddress (Mac48Address ("00:00:00:00:00:07")); + routerC->AddDevice (fwDev2routerC); + } + net4.Add (fwDev2routerC); + + // Rx node + Ptr rxDev; + { // first interface + rxDev = CreateObject (); + rxDev->SetAddress (Mac48Address ("00:00:00:00:00:08")); + rxNode->AddDevice (rxDev); + } + net4.Add (rxDev); + + // link the channels + Ptr channel1 = CreateObject (); + txDev->SetChannel (channel1); + fwDev1routerA->SetChannel (channel1); + + Ptr channel2 = CreateObject (); + fwDev2routerA->SetChannel (channel2); + fwDev1routerB->SetChannel (channel2); + + Ptr channel3 = CreateObject (); + fwDev2routerB->SetChannel (channel3); + fwDev1routerC->SetChannel (channel3); + + Ptr channel4 = CreateObject (); + fwDev2routerC->SetChannel (channel4); + rxDev->SetChannel (channel4); + + // Setup IPv4 addresses and forwarding + Ipv4AddressHelper ipv4; + + ipv4.SetBase (Ipv4Address ("10.0.1.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic1 = ipv4.Assign (net1); + + ipv4.SetBase (Ipv4Address ("192.168.0.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic2 = ipv4.Assign (net2); + + ipv4.SetBase (Ipv4Address ("192.168.1.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic3 = ipv4.Assign (net3); + + ipv4.SetBase (Ipv4Address ("10.0.2.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic4 = ipv4.Assign (net4); + + Ptr staticRouting; + staticRouting = Ipv4RoutingHelper::GetRouting (txNode->GetObject ()->GetRoutingProtocol ()); + staticRouting->SetDefaultRoute ("10.0.1.2", 1 ); + staticRouting = Ipv4RoutingHelper::GetRouting (rxNode->GetObject ()->GetRoutingProtocol ()); + staticRouting->SetDefaultRoute ("10.0.2.1", 1 ); + + // Create the UDP sockets + Ptr rxSocketFactory = rxNode->GetObject (); + Ptr rxSocket = rxSocketFactory->CreateSocket (); + NS_TEST_EXPECT_MSG_EQ (rxSocket->Bind (InetSocketAddress (Ipv4Address ("10.0.2.2"), 1234)), 0, "trivial"); + rxSocket->SetRecvCallback (MakeCallback (&Ipv4RipTest::ReceivePkt, this)); + + Ptr txSocketFactory = txNode->GetObject (); + Ptr txSocket = txSocketFactory->CreateSocket (); + txSocket->SetAllowBroadcast (true); + + // ------ Now the tests ------------ + + // Unicast test + SendData (txSocket, "10.0.2.2"); + NS_TEST_EXPECT_MSG_EQ (m_receivedPacket->GetSize (), 123, "IPv4 RIP should work."); + + m_receivedPacket->RemoveAllByteTags (); + + Simulator::Destroy (); +} + +// Ipv4RipCountToInfinityTest + +class Ipv4RipCountToInfinityTest : public TestCase +{ + Ptr m_receivedPacket; + void DoSendData (Ptr socket, std::string to); + void SendData (Ptr socket, std::string to); + +public: + virtual void DoRun (void); + Ipv4RipCountToInfinityTest (); + + void ReceivePkt (Ptr socket); +}; + +Ipv4RipCountToInfinityTest::Ipv4RipCountToInfinityTest () + : TestCase ("RIP counting to infinity") +{ +} + +void Ipv4RipCountToInfinityTest::ReceivePkt (Ptr socket) +{ + uint32_t availableData; + availableData = socket->GetRxAvailable (); + m_receivedPacket = socket->Recv (std::numeric_limits::max (), 0); + NS_ASSERT (availableData == m_receivedPacket->GetSize ()); + //cast availableData to void, to suppress 'availableData' set but not used + //compiler warning + (void) availableData; +} + +void +Ipv4RipCountToInfinityTest::DoSendData (Ptr socket, std::string to) +{ + Address realTo = InetSocketAddress (Ipv4Address (to.c_str ()), 1234); + NS_TEST_EXPECT_MSG_EQ (socket->SendTo (Create (123), 0, realTo), + 123, "100"); +} + +void +Ipv4RipCountToInfinityTest::SendData (Ptr socket, std::string to) +{ + m_receivedPacket = Create (); + Simulator::ScheduleWithContext (socket->GetNode ()->GetId (), Seconds (60), + &Ipv4RipCountToInfinityTest::DoSendData, this, socket, to); + Simulator::Stop (Seconds (66)); + Simulator::Run (); +} + +void +Ipv4RipCountToInfinityTest::DoRun (void) +{ + // Create topology + + Ptr txNode = CreateObject (); + Ptr rxNode = CreateObject (); + Ptr routerA = CreateObject (); + Ptr routerB = CreateObject (); + Ptr routerC = CreateObject (); + + NodeContainer nodes (txNode, rxNode); + NodeContainer routers (routerA, routerB, routerC); + NodeContainer all (nodes, routers); + + RipHelper ripNgRouting; + // Change the router's interface metric to 10, must not send packets (count to infinity) + // note: Interface 0 is the loopback. + ripNgRouting.SetInterfaceMetric (routerA, 2, 10); + ripNgRouting.SetInterfaceMetric (routerB, 1, 10); + ripNgRouting.SetInterfaceMetric (routerB, 2, 10); + ripNgRouting.SetInterfaceMetric (routerC, 1, 10); + + InternetStackHelper internetv6routers; + internetv6routers.SetRoutingHelper (ripNgRouting); + internetv6routers.Install (routers); + + InternetStackHelper internetv6nodes; + internetv6nodes.Install (nodes); + + NetDeviceContainer net1; + NetDeviceContainer net2; + NetDeviceContainer net3; + NetDeviceContainer net4; + + // Sender Node + Ptr txDev; + { + txDev = CreateObject (); + txDev->SetAddress (Mac48Address ("00:00:00:00:00:01")); + txNode->AddDevice (txDev); + } + net1.Add (txDev); + + // Router A + Ptr fwDev1routerA, fwDev2routerA; + { // first interface + fwDev1routerA = CreateObject (); + fwDev1routerA->SetAddress (Mac48Address ("00:00:00:00:00:02")); + routerA->AddDevice (fwDev1routerA); + } + net1.Add (fwDev1routerA); + + { // second interface + fwDev2routerA = CreateObject (); + fwDev2routerA->SetAddress (Mac48Address ("00:00:00:00:00:03")); + routerA->AddDevice (fwDev2routerA); + } + net2.Add (fwDev2routerA); + + // Router B + Ptr fwDev1routerB, fwDev2routerB; + { // first interface + fwDev1routerB = CreateObject (); + fwDev1routerB->SetAddress (Mac48Address ("00:00:00:00:00:04")); + routerB->AddDevice (fwDev1routerB); + } + net2.Add (fwDev1routerB); + + { // second interface + fwDev2routerB = CreateObject (); + fwDev2routerB->SetAddress (Mac48Address ("00:00:00:00:00:05")); + routerB->AddDevice (fwDev2routerB); + } + net3.Add (fwDev2routerB); + + // Router C + Ptr fwDev1routerC, fwDev2routerC; + { // first interface + fwDev1routerC = CreateObject (); + fwDev1routerC->SetAddress (Mac48Address ("00:00:00:00:00:06")); + routerC->AddDevice (fwDev1routerC); + } + net3.Add (fwDev1routerC); + + { // second interface + fwDev2routerC = CreateObject (); + fwDev2routerC->SetAddress (Mac48Address ("00:00:00:00:00:07")); + routerC->AddDevice (fwDev2routerC); + } + net4.Add (fwDev2routerC); + + // Rx node + Ptr rxDev; + { // first interface + rxDev = CreateObject (); + rxDev->SetAddress (Mac48Address ("00:00:00:00:00:08")); + rxNode->AddDevice (rxDev); + } + net4.Add (rxDev); + + // link the channels + Ptr channel1 = CreateObject (); + txDev->SetChannel (channel1); + fwDev1routerA->SetChannel (channel1); + + Ptr channel2 = CreateObject (); + fwDev2routerA->SetChannel (channel2); + fwDev1routerB->SetChannel (channel2); + + Ptr channel3 = CreateObject (); + fwDev2routerB->SetChannel (channel3); + fwDev1routerC->SetChannel (channel3); + + Ptr channel4 = CreateObject (); + fwDev2routerC->SetChannel (channel4); + rxDev->SetChannel (channel4); + + // Setup IPv4 addresses and forwarding + Ipv4AddressHelper ipv4; + + ipv4.SetBase (Ipv4Address ("10.0.1.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic1 = ipv4.Assign (net1); + + ipv4.SetBase (Ipv4Address ("192.168.0.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic2 = ipv4.Assign (net2); + + ipv4.SetBase (Ipv4Address ("192.168.1.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic3 = ipv4.Assign (net3); + + ipv4.SetBase (Ipv4Address ("10.0.2.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic4 = ipv4.Assign (net4); + + Ptr staticRouting; + staticRouting = Ipv4RoutingHelper::GetRouting (txNode->GetObject ()->GetRoutingProtocol ()); + staticRouting->SetDefaultRoute ("10.0.1.2", 1 ); + staticRouting = Ipv4RoutingHelper::GetRouting (rxNode->GetObject ()->GetRoutingProtocol ()); + staticRouting->SetDefaultRoute ("10.0.2.1", 1 ); + + + // Create the UDP sockets + Ptr rxSocketFactory = rxNode->GetObject (); + Ptr rxSocket = rxSocketFactory->CreateSocket (); + NS_TEST_EXPECT_MSG_EQ (rxSocket->Bind (InetSocketAddress (Ipv4Address ("10.0.2.2"), 1234)), 0, "trivial"); + rxSocket->SetRecvCallback (MakeCallback (&Ipv4RipCountToInfinityTest::ReceivePkt, this)); + + Ptr txSocketFactory = txNode->GetObject (); + Ptr txSocket = txSocketFactory->CreateSocket (); + txSocket->SetAllowBroadcast (true); + + // ------ Now the tests ------------ + + SendData (txSocket, "10.0.2.2"); + NS_TEST_EXPECT_MSG_EQ (m_receivedPacket->GetSize (), 0, "RIP counting to infinity."); + + Simulator::Destroy (); +} + +// Ipv4RipSplitHorizonStrategyTest + +class Ipv4RipSplitHorizonStrategyTest : public TestCase +{ + Rip::SplitHorizonType_e m_setStrategy; + Rip::SplitHorizonType_e m_detectedStrategy; + +public: + virtual void DoRun (void); + Ipv4RipSplitHorizonStrategyTest (Rip::SplitHorizonType_e strategy); + + void ReceivePktProbe (Ptr socket); +}; + +Ipv4RipSplitHorizonStrategyTest::Ipv4RipSplitHorizonStrategyTest (Rip::SplitHorizonType_e strategy) + : TestCase ("RIP Split Horizon strategy") +{ + m_setStrategy = strategy; +} + +void Ipv4RipSplitHorizonStrategyTest::ReceivePktProbe (Ptr socket) +{ + uint32_t availableData; + availableData = socket->GetRxAvailable (); + Ptr receivedPacketProbe = socket->Recv (std::numeric_limits::max (), 0); + NS_ASSERT (availableData == receivedPacketProbe->GetSize ()); + SocketAddressTag tag; + receivedPacketProbe->RemovePacketTag (tag); + Ipv4Address senderAddress = InetSocketAddress::ConvertFrom (tag.GetAddress ()).GetIpv4 (); + + if (senderAddress == "192.168.0.2") + { + RipHeader hdr; + receivedPacketProbe->RemoveHeader (hdr); + + std::list rtes = hdr.GetRteList (); + + // validate the RTEs before processing + for (std::list::iterator iter = rtes.begin (); + iter != rtes.end (); iter++) + { + if (iter->GetPrefix () == "10.0.1.0") + { + bool correct = false; + if (iter->GetRouteMetric () == 16) + { + correct = true; + m_detectedStrategy = Rip::POISON_REVERSE; + } + else if (iter->GetRouteMetric () == 2) + { + correct = true; + m_detectedStrategy = Rip::NO_SPLIT_HORIZON; + } + NS_TEST_EXPECT_MSG_EQ (correct, true, "RIP: unexpected metric value: " << iter->GetRouteMetric ()); + } + } + } + + //cast availableData to void, to suppress 'availableData' set but not used + //compiler warning + (void) availableData; +} + +void +Ipv4RipSplitHorizonStrategyTest::DoRun (void) +{ + // Create topology + + Ptr fakeNode = CreateObject (); + Ptr listener = CreateObject (); + + Ptr routerA = CreateObject (); + Ptr routerB = CreateObject (); + + NodeContainer listeners (listener, fakeNode); + NodeContainer routers (routerA, routerB); + NodeContainer all (routers, listeners); + + RipHelper ripNgRouting; + ripNgRouting.Set ("SplitHorizon", EnumValue (m_setStrategy)); + + InternetStackHelper internetRouters; + internetRouters.SetRoutingHelper (ripNgRouting); + internetRouters.Install (routers); + + InternetStackHelper internetNodes; + internetNodes.Install (listeners); + + NetDeviceContainer net0; + NetDeviceContainer net1; + + // Fake Node + Ptr silentDev; + { + silentDev = CreateObject (); + silentDev->SetAddress (Mac48Address ("00:00:00:00:00:01")); + fakeNode->AddDevice (silentDev); + } + net0.Add (silentDev); + + // Router A + Ptr silentDevRouterA, fwDevRouterA; + { // silent interface + silentDevRouterA = CreateObject (); + silentDevRouterA->SetAddress (Mac48Address ("00:00:00:00:00:02")); + routerA->AddDevice (silentDevRouterA); + } + net0.Add (silentDevRouterA); + + { // first interface + fwDevRouterA = CreateObject (); + fwDevRouterA->SetAddress (Mac48Address ("00:00:00:00:00:03")); + routerA->AddDevice (fwDevRouterA); + } + net1.Add (fwDevRouterA); + + // Router B + Ptr fwDevRouterB; + { // first interface + fwDevRouterB = CreateObject (); + fwDevRouterB->SetAddress (Mac48Address ("00:00:00:00:00:04")); + routerB->AddDevice (fwDevRouterB); + } + net1.Add (fwDevRouterB); + + // listener A + Ptr listenerDev; + { + listenerDev = CreateObject (); + listenerDev->SetAddress (Mac48Address ("00:00:00:00:00:05")); + listener->AddDevice (listenerDev); + } + net1.Add (listenerDev); + + // link the channels + Ptr channel0 = CreateObject (); + silentDev->SetChannel (channel0); + silentDevRouterA->SetChannel (channel0); + + Ptr channel1 = CreateObject (); + fwDevRouterA->SetChannel (channel1); + fwDevRouterB->SetChannel (channel1); + listenerDev->SetChannel (channel1); + + // Setup IPv6 addresses and forwarding + Ipv4AddressHelper ipv4; + + ipv4.SetBase (Ipv4Address ("10.0.1.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic0 = ipv4.Assign (net0); + + ipv4.SetBase (Ipv4Address ("192.168.0.0"), Ipv4Mask ("255.255.255.0")); + Ipv4InterfaceContainer iic1 = ipv4.Assign (net1); + + // Create the UDP sockets + Ptr rxSocketFactory = listener->GetObject (); + Ptr rxSocket = rxSocketFactory->CreateSocket (); + NS_TEST_EXPECT_MSG_EQ (rxSocket->Bind (InetSocketAddress (Ipv4Address ("224.0.0.9"), 520)), 0, "trivial"); + rxSocket->BindToNetDevice (listenerDev); + rxSocket->SetRecvCallback (MakeCallback (&Ipv4RipSplitHorizonStrategyTest::ReceivePktProbe, this)); + + // ------ Now the tests ------------ + + // If the strategy is Split Horizon, then no packet will be received. + m_detectedStrategy = Rip::SPLIT_HORIZON; + + Simulator::Stop (Seconds (66)); + Simulator::Run (); + NS_TEST_EXPECT_MSG_EQ (m_detectedStrategy, m_setStrategy, "RIP counting to infinity."); + + Simulator::Destroy (); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class Ipv4RipTestSuite : public TestSuite +{ +public: + Ipv4RipTestSuite () : TestSuite ("ipv4-rip", UNIT) + { + AddTestCase (new Ipv4RipTest, TestCase::QUICK); + AddTestCase (new Ipv4RipCountToInfinityTest, TestCase::QUICK); + AddTestCase (new Ipv4RipSplitHorizonStrategyTest (Rip::POISON_REVERSE), TestCase::QUICK); + AddTestCase (new Ipv4RipSplitHorizonStrategyTest (Rip::SPLIT_HORIZON), TestCase::QUICK); + AddTestCase (new Ipv4RipSplitHorizonStrategyTest (Rip::NO_SPLIT_HORIZON), TestCase::QUICK); + } +} g_ipv4ripTestSuite; diff --git a/src/internet/wscript b/src/internet/wscript index f9c9657bd..921f191ff 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -206,6 +206,9 @@ def build(bld): 'model/ripng.cc', 'model/ripng-header.cc', 'helper/ripng-helper.cc', + 'model/rip.cc', + 'model/rip-header.cc', + 'helper/rip-helper.cc', ] internet_test = bld.create_ns3_module_test_library('internet') @@ -256,6 +259,8 @@ def build(bld): 'test/codel-queue-test-suite.cc', 'test/tcp-endpoint-bug2211.cc', 'test/tcp-datasentcb-test.cc', + 'test/ipv4-rip-test.cc', + ] privateheaders = bld(features='ns3privateheader') privateheaders.module = 'internet' @@ -351,6 +356,9 @@ def build(bld): 'model/ripng.h', 'model/ripng-header.h', 'helper/ripng-helper.h', + 'model/rip.h', + 'model/rip-header.h', + 'helper/rip-helper.h', ] if bld.env['NSC_ENABLED']: