/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014 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 <tommaso.pecorella@unifi.it>
*/
#include <iomanip>
#include "ripng.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/ipv6-route.h"
#include "ns3/node.h"
#include "ns3/names.h"
#include "ns3/ripng-header.h"
#include "ns3/udp-header.h"
#include "ns3/enum.h"
#include "ns3/ipv6-packet-info-tag.h"
#define RIPNG_ALL_NODE "ff02::9"
#define RIPNG_PORT 521
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("RipNg");
NS_OBJECT_ENSURE_REGISTERED (RipNg);
RipNg::RipNg ()
: m_ipv6 (0), m_splitHorizonStrategy (RipNg::POISON_REVERSE), m_initialized (false)
{
m_rng = CreateObject<UniformRandomVariable> ();
}
RipNg::~RipNg ()
{
}
TypeId
RipNg::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RipNg")
.SetParent<Ipv6RoutingProtocol> ()
.AddConstructor<RipNg> ()
.AddAttribute ("UnsolicitedRoutingUpdate", "The time between two Unsolicited Routing Updates.",
TimeValue (Seconds(30)),
MakeTimeAccessor (&RipNg::m_unsolicitedUpdate),
MakeTimeChecker ())
.AddAttribute ("StartupDelay", "Maximum random delay for protocol startup (send route requests).",
TimeValue (Seconds(1)),
MakeTimeAccessor (&RipNg::m_startupDelay),
MakeTimeChecker ())
.AddAttribute ("TimeoutDelay", "The delay to invalidate a route.",
TimeValue (Seconds(180)),
MakeTimeAccessor (&RipNg::m_timeoutDelay),
MakeTimeChecker ())
.AddAttribute ("GarbageCollectionDelay", "The delay to delete an expired route.",
TimeValue (Seconds(120)),
MakeTimeAccessor (&RipNg::m_garbageCollectionDelay),
MakeTimeChecker ())
.AddAttribute ("MinTriggeredCooldown", "Min cooldown delay after a Triggered Update.",
TimeValue (Seconds(1)),
MakeTimeAccessor (&RipNg::m_minTriggeredUpdateDelay),
MakeTimeChecker ())
.AddAttribute ("MaxTriggeredCooldown", "Max cooldown delay after a Triggered Update.",
TimeValue (Seconds(5)),
MakeTimeAccessor (&RipNg::m_maxTriggeredUpdateDelay),
MakeTimeChecker ())
.AddAttribute ("SplitHorizon", "Split Horizon strategy.",
EnumValue (RipNg::POISON_REVERSE),
MakeEnumAccessor (&RipNg::m_splitHorizonStrategy),
MakeEnumChecker (RipNg::NO_SPLIT_HORIZON, "NoSplitHorizon",
RipNg::SPLIT_HORIZON, "SplitHorizon",
RipNg::POISON_REVERSE, "PoisonReverse"))
;
return tid;
}
int64_t RipNg::AssignStreams (int64_t stream)
{
NS_LOG_FUNCTION (this << stream);
m_rng->SetStream (stream);
return 1;
}
void RipNg::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, &RipNg::SendUnsolicitedRouteUpdate, this);
for (uint32_t i = 0 ; i < m_ipv6->GetNInterfaces (); i++)
{
bool activeInterface = false;
if (m_interfaceExclusions.find (i) == m_interfaceExclusions.end ())
{
activeInterface = true;
}
for (uint32_t j = 0; j < m_ipv6->GetNAddresses (i); j++)
{
Ipv6InterfaceAddress address = m_ipv6->GetAddress (i, j);
if (address.GetScope() == Ipv6InterfaceAddress::LINKLOCAL && activeInterface == true)
{
NS_LOG_LOGIC ("RIPng: adding socket to " << address.GetAddress ());
TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
Ptr<Node> theNode = GetObject<Node> ();
Ptr<Socket> socket = Socket::CreateSocket (theNode, tid);
Inet6SocketAddress local = Inet6SocketAddress (address.GetAddress (), RIPNG_PORT);
int ret = socket->Bind (local);
NS_ASSERT_MSG (ret == 0, "Bind unsuccessful");
socket->BindToNetDevice (m_ipv6->GetNetDevice (i));
socket->ShutdownRecv ();
socket->SetIpv6RecvHopLimit (true);
m_sendSocketList[socket] = i;
}
else if (m_ipv6->GetAddress (i, j).GetScope() == Ipv6InterfaceAddress::GLOBAL)
{
addedGlobal = true;
}
}
}
if (!m_recvSocket)
{
NS_LOG_LOGIC ("RIPng: adding receiving socket");
TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
Ptr<Node> theNode = GetObject<Node> ();
m_recvSocket = Socket::CreateSocket (theNode, tid);
Inet6SocketAddress local = Inet6SocketAddress (Ipv6Address::GetAny (), RIPNG_PORT);
m_recvSocket->Bind (local);
m_recvSocket->SetRecvCallback (MakeCallback (&RipNg::Receive, this));
m_recvSocket->SetIpv6RecvHopLimit (true);
m_recvSocket->SetRecvPktInfo (true);
}
if (addedGlobal)
{
Time delay = Seconds (m_rng->GetValue (m_minTriggeredUpdateDelay.GetSeconds (), m_maxTriggeredUpdateDelay.GetSeconds ()));
m_nextTriggeredUpdate = Simulator::Schedule (delay, &RipNg::DoSendRouteUpdate, this, false);
}
delay = Seconds (m_rng->GetValue (0.01, m_startupDelay.GetSeconds ()));
m_nextTriggeredUpdate = Simulator::Schedule (delay, &RipNg::SendRouteRequest, this);
Ipv6RoutingProtocol::DoInitialize ();
}
Ptr<Ipv6Route> RipNg::RouteOutput (Ptr<Packet> p, const Ipv6Header &header, Ptr<NetDevice> oif, Socket::SocketErrno &sockerr)
{
NS_LOG_FUNCTION (this << header << oif);
Ipv6Address destination = header.GetDestinationAddress ();
Ptr<Ipv6Route> 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 RipNg::RouteInput (Ptr<const Packet> p, const Ipv6Header &header, Ptr<const NetDevice> idev,
UnicastForwardCallback ucb, MulticastForwardCallback mcb,
LocalDeliverCallback lcb, ErrorCallback ecb)
{
NS_LOG_FUNCTION (this << p << header << header.GetSourceAddress () << header.GetDestinationAddress () << idev);
NS_ASSERT (m_ipv6 != 0);
// Check if input device supports IP
NS_ASSERT (m_ipv6->GetInterfaceForDevice (idev) >= 0);
uint32_t iif = m_ipv6->GetInterfaceForDevice (idev);
Ipv6Address dst = header.GetDestinationAddress ();
if (dst.IsMulticast ())
{
NS_LOG_LOGIC ("Multicast route not supported by RIPng");
return false; // Let other routing protocols try to handle this
}
/// \todo Configurable option to enable \RFC{1222} Strong End System Model
// Right now, we will be permissive and allow a source to send us
// a packet to one of our other interface addresses; that is, the
// destination unicast address does not match one of the iif addresses,
// but we check our other interfaces. This could be an option
// (to remove the outer loop immediately below and just check iif).
for (uint32_t j = 0; j < m_ipv6->GetNInterfaces (); j++)
{
for (uint32_t i = 0; i < m_ipv6->GetNAddresses (j); i++)
{
Ipv6InterfaceAddress iaddr = m_ipv6->GetAddress (j, i);
Ipv6Address addr = iaddr.GetAddress ();
if (addr.IsEqual (header.GetDestinationAddress ()))
{
if (j == iif)
{
NS_LOG_LOGIC ("For me (destination " << addr << " match)");
}
else
{
NS_LOG_LOGIC ("For me (destination " << addr << " match) on another interface " << header.GetDestinationAddress ());
}
lcb (p, header, iif);
return true;
}
NS_LOG_LOGIC ("Address " << addr << " not a match");
}
}
// Check if input device supports IP forwarding
if (m_ipv6->IsForwarding (iif) == false)
{
NS_LOG_LOGIC ("Forwarding disabled for this interface");
ecb (p, header, Socket::ERROR_NOROUTETOHOST);
return false;
}
// Next, try to find a route
NS_LOG_LOGIC ("Unicast destination");
Ptr<Ipv6Route> rtentry = Lookup (header.GetDestinationAddress ());
if (rtentry != 0)
{
NS_LOG_LOGIC ("Found unicast destination- calling unicast callback");
ucb (idev, 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 RipNg::NotifyInterfaceUp (uint32_t i)
{
NS_LOG_FUNCTION (this << i);
for (uint32_t j = 0; j < m_ipv6->GetNAddresses (i); j++)
{
Ipv6InterfaceAddress address = m_ipv6->GetAddress (i, j);
Ipv6Prefix networkMask = address.GetPrefix ();
Ipv6Address networkAddress = address.GetAddress ().CombinePrefix (networkMask);
if (address != Ipv6Address () && networkMask != Ipv6Prefix ())
{
if (networkMask == Ipv6Prefix (128))
{
/* host route */
AddNetworkRouteTo (networkAddress, Ipv6Prefix::GetOnes (), 0);
}
else
{
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;
}
for (uint32_t j = 0; j < m_ipv6->GetNAddresses (i); j++)
{
Ipv6InterfaceAddress address = m_ipv6->GetAddress (i, j);
Ipv6Address networkAddress = address.GetAddress ().CombinePrefix (address.GetPrefix ());
Ipv6Prefix networkMask = address.GetPrefix ();
AddNetworkRouteTo (networkAddress, networkMask, i);
if (address.GetScope() == Ipv6InterfaceAddress::LINKLOCAL && sendSocketFound == false && activeInterface == true)
{
NS_LOG_LOGIC ("RIPng: adding sending socket to " << address.GetAddress ());
TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
Ptr<Node> theNode = GetObject<Node> ();
Ptr<Socket> socket = Socket::CreateSocket (theNode, tid);
Inet6SocketAddress local = Inet6SocketAddress (address.GetAddress (), RIPNG_PORT);
socket->Bind (local);
socket->BindToNetDevice (m_ipv6->GetNetDevice (i));
socket->ShutdownRecv ();
socket->SetIpv6RecvHopLimit (true);
m_sendSocketList[socket] = i;
}
else if (address.GetScope() == Ipv6InterfaceAddress::GLOBAL)
{
SendTriggeredRouteUpdate ();
}
}
if (!m_recvSocket)
{
NS_LOG_LOGIC ("RIPng: adding receiving socket");
TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
Ptr<Node> theNode = GetObject<Node> ();
m_recvSocket = Socket::CreateSocket (theNode, tid);
Inet6SocketAddress local = Inet6SocketAddress (Ipv6Address::GetAny (), RIPNG_PORT);
m_recvSocket->Bind (local);
m_recvSocket->SetRecvCallback (MakeCallback (&RipNg::Receive, this));
m_recvSocket->SetIpv6RecvHopLimit (true);
m_recvSocket->SetRecvPktInfo (true);
}
}
void RipNg::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 RipNg::NotifyAddAddress (uint32_t interface, Ipv6InterfaceAddress address)
{
NS_LOG_FUNCTION (this << interface << address);
if (!m_ipv6->IsUp (interface))
{
return;
}
if (m_interfaceExclusions.find (interface) != m_interfaceExclusions.end ())
{
return;
}
Ipv6Address networkAddress = address.GetAddress ().CombinePrefix (address.GetPrefix ());
Ipv6Prefix networkMask = address.GetPrefix ();
if (address.GetAddress () != Ipv6Address () && address.GetPrefix () != Ipv6Prefix ())
{
AddNetworkRouteTo (networkAddress, networkMask, interface);
}
SendTriggeredRouteUpdate ();
}
void RipNg::NotifyRemoveAddress (uint32_t interface, Ipv6InterfaceAddress address)
{
NS_LOG_FUNCTION (this << interface << address);
if (!m_ipv6->IsUp (interface))
{
return;
}
if (address.GetScope() != Ipv6InterfaceAddress::GLOBAL)
{
return;
}
Ipv6Address networkAddress = address.GetAddress ().CombinePrefix (address.GetPrefix ());
Ipv6Prefix networkMask = address.GetPrefix ();
// 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->GetDestNetworkPrefix () == networkMask)
{
InvalidateRoute (it->first);
}
}
if (m_interfaceExclusions.find (interface) == m_interfaceExclusions.end ())
{
SendTriggeredRouteUpdate ();
}
}
void RipNg::NotifyAddRoute (Ipv6Address dst, Ipv6Prefix mask, Ipv6Address nextHop, uint32_t interface, Ipv6Address prefixToUse)
{
NS_LOG_INFO (this << dst << mask << nextHop << interface << prefixToUse);
// \todo this can be used to add delegate routes
}
void RipNg::NotifyRemoveRoute (Ipv6Address dst, Ipv6Prefix mask, Ipv6Address nextHop, uint32_t interface, Ipv6Address prefixToUse)
{
NS_LOG_FUNCTION (this << dst << mask << nextHop << interface);
// \todo this can be used to delete delegate routes
}
void RipNg::SetIpv6 (Ptr<Ipv6> ipv6)
{
NS_LOG_FUNCTION (this << ipv6);
NS_ASSERT (m_ipv6 == 0 && ipv6 != 0);
uint32_t i = 0;
m_ipv6 = ipv6;
for (i = 0; i < m_ipv6->GetNInterfaces (); i++)
{
if (m_ipv6->IsUp (i))
{
NotifyInterfaceUp (i);
}
else
{
NotifyInterfaceDown (i);
}
}
}
void RipNg::PrintRoutingTable (Ptr<OutputStreamWrapper> stream) const
{
NS_LOG_FUNCTION (this << stream);
std::ostream* os = stream->GetStream ();
*os << "Node: " << m_ipv6->GetObject<Node> ()->GetId ()
<< " Time: " << Simulator::Now ().GetSeconds () << "s "
<< "Ipv6 RIPng table" << std::endl;
if (!m_routes.empty ())
{
*os << "Destination Next Hop Flag Met Ref Use If" << std::endl;
for (RoutesCI it = m_routes.begin (); it != m_routes.end (); it++)
{
RipNgRoutingTableEntry* route = it->first;
RipNgRoutingTableEntry::Status_e status = route->GetRouteStatus();
if (status == RipNgRoutingTableEntry::RIPNG_VALID)
{
std::ostringstream dest, gw, mask, flags;
dest << route->GetDest () << "/" << int(route->GetDestNetworkPrefix ().GetPrefixLength ());
*os << std::setiosflags (std::ios::left) << std::setw (31) << dest.str ();
gw << route->GetGateway ();
*os << std::setiosflags (std::ios::left) << std::setw (27) << gw.str ();
flags << "U";
if (route->IsHost ())
{
flags << "H";
}
else if (route->IsGateway ())
{
flags << "G";
}
*os << std::setiosflags (std::ios::left) << std::setw (5) << flags.str ();
*os << std::setiosflags (std::ios::left) << std::setw (4) << int(route->GetRouteMetric ());
// Ref ct not implemented
*os << "-" << " ";
// Use not implemented
*os << "-" << " ";
if (Names::FindName (m_ipv6->GetNetDevice (route->GetInterface ())) != "")
{
*os << Names::FindName (m_ipv6->GetNetDevice (route->GetInterface ()));
}
else
{
*os << route->GetInterface ();
}
*os << std::endl;
}
}
}
}
void RipNg::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_ipv6 = 0;
Ipv6RoutingProtocol::DoDispose ();
}
Ptr<Ipv6Route> RipNg::Lookup (Ipv6Address dst, Ptr<NetDevice> interface)
{
NS_LOG_FUNCTION (this << dst << interface);
Ptr<Ipv6Route> rtentry = 0;
uint16_t longestMask = 0;
/* when sending on link-local multicast, there have to be interface specified */
if (dst.IsLinkLocalMulticast ())
{
NS_ASSERT_MSG (interface, "Try to send on link-local multicast address, and no interface index is given!");
rtentry = Create<Ipv6Route> ();
rtentry->SetSource (m_ipv6->SourceAddressSelection (m_ipv6->GetInterfaceForDevice (interface), dst));
rtentry->SetDestination (dst);
rtentry->SetGateway (Ipv6Address::GetZero ());
rtentry->SetOutputDevice (interface);
return rtentry;
}
for (RoutesI it = m_routes.begin (); it != m_routes.end (); it++)
{
RipNgRoutingTableEntry* j = it->first;
if (j->GetRouteStatus () == RipNgRoutingTableEntry::RIPNG_VALID)
{
Ipv6Prefix mask = j->GetDestNetworkPrefix ();
uint16_t maskLen = mask.GetPrefixLength ();
Ipv6Address 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_ipv6->GetNetDevice (j->GetInterface ()))
{
if (maskLen < longestMask)
{
NS_LOG_LOGIC ("Previous match longer, skipping");
continue;
}
longestMask = maskLen;
Ipv6RoutingTableEntry* route = j;
uint32_t interfaceIdx = route->GetInterface ();
rtentry = Create<Ipv6Route> ();
if (route->GetGateway ().IsAny ())
{
rtentry->SetSource (m_ipv6->SourceAddressSelection (interfaceIdx, route->GetDest ()));
}
else if (route->GetDest ().IsAny ()) /* default route */
{
rtentry->SetSource (m_ipv6->SourceAddressSelection (interfaceIdx, route->GetPrefixToUse ().IsAny () ? dst : route->GetPrefixToUse ()));
}
else
{
rtentry->SetSource (m_ipv6->SourceAddressSelection (interfaceIdx, route->GetDest ()));
}
rtentry->SetDestination (route->GetDest ());
rtentry->SetGateway (route->GetGateway ());
rtentry->SetOutputDevice (m_ipv6->GetNetDevice (interfaceIdx));
}
}
}
}
if (rtentry)
{
NS_LOG_LOGIC ("Matching route via " << rtentry->GetDestination () << " (through " << rtentry->GetGateway () << ") at the end");
}
return rtentry;
}
void RipNg::AddNetworkRouteTo (Ipv6Address network, Ipv6Prefix networkPrefix, Ipv6Address nextHop, uint32_t interface, Ipv6Address prefixToUse)
{
NS_LOG_FUNCTION (this << network << networkPrefix << nextHop << interface << prefixToUse);
if (nextHop.IsLinkLocal())
{
NS_LOG_WARN ("Ripng::AddNetworkRouteTo - Next hop should be link-local");
}
RipNgRoutingTableEntry* route = new RipNgRoutingTableEntry (network, networkPrefix, nextHop, interface, prefixToUse);
route->SetRouteMetric (1);
route->SetRouteStatus (RipNgRoutingTableEntry::RIPNG_VALID);
route->SetRouteChanged (true);
m_routes.push_back (std::make_pair (route, EventId ()));
}
void RipNg::AddNetworkRouteTo (Ipv6Address network, Ipv6Prefix networkPrefix, uint32_t interface)
{
NS_LOG_FUNCTION (this << network << networkPrefix << interface);
RipNgRoutingTableEntry* route = new RipNgRoutingTableEntry (network, networkPrefix, interface);
route->SetRouteMetric (1);
route->SetRouteStatus (RipNgRoutingTableEntry::RIPNG_VALID);
route->SetRouteChanged (true);
m_routes.push_back (std::make_pair (route, EventId ()));
}
void RipNg::InvalidateRoute (RipNgRoutingTableEntry *route)
{
NS_LOG_FUNCTION (this << *route);
for (RoutesI it = m_routes.begin (); it != m_routes.end (); it++)
{
if (it->first == route)
{
route->SetRouteStatus (RipNgRoutingTableEntry::RIPNG_INVALID);
route->SetRouteMetric (16);
route->SetRouteChanged (true);
if (it->second.IsRunning ())
{
it->second.Cancel ();
}
it->second = Simulator::Schedule (m_garbageCollectionDelay, &RipNg::DeleteRoute, this, route);
return;
}
}
NS_ABORT_MSG ("Ripng::InvalidateRoute - cannot find the route to update");
}
void RipNg::DeleteRoute (RipNgRoutingTableEntry *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 ("Ripng::DeleteRoute - cannot find the route to delete");
}
void RipNg::Receive (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
Ptr<Packet> packet = socket->Recv ();
NS_LOG_INFO ("Received " << *packet);
Ipv6PacketInfoTag interfaceInfo;
if (!packet->RemovePacketTag (interfaceInfo))
{
NS_ABORT_MSG ("No incoming interface on RIPng message, aborting.");
}
uint32_t incomingIf = interfaceInfo.GetRecvIf ();
Ptr<Node> node = this->GetObject<Node> ();
Ptr<NetDevice> dev = node->GetDevice (incomingIf);
uint32_t ipInterfaceIndex = m_ipv6->GetInterfaceForDevice (dev);
SocketIpv6HopLimitTag hoplimitTag;
if (!packet->RemovePacketTag (hoplimitTag))
{
NS_ABORT_MSG ("No incoming Hop Count on RIPng message, aborting.");
}
uint8_t hopLimit = hoplimitTag.GetHopLimit ();
SocketAddressTag tag;
if (!packet->RemovePacketTag (tag))
{
NS_ABORT_MSG ("No incoming sender address on RIPng message, aborting.");
}
Ipv6Address senderAddress = Inet6SocketAddress::ConvertFrom (tag.GetAddress ()).GetIpv6 ();
uint16_t senderPort = Inet6SocketAddress::ConvertFrom (tag.GetAddress ()).GetPort ();
int32_t interfaceForAddress = m_ipv6->GetInterfaceForAddress (senderAddress);
if (interfaceForAddress != -1)
{
NS_LOG_LOGIC ("Ignoring a packet sent by myself.");
return;
}
RipNgHeader hdr;
packet->RemoveHeader (hdr);
if (hdr.GetCommand () == RipNgHeader::RESPONSE)
{
HandleResponses (hdr, senderAddress, ipInterfaceIndex, hopLimit);
}
else if (hdr.GetCommand () == RipNgHeader::REQUEST)
{
HandleRequests (hdr, senderAddress, senderPort, ipInterfaceIndex, hopLimit);
}
else
{
NS_LOG_LOGIC ("Ignoring message with unknown command: " << int (hdr.GetCommand ()));
}
return;
}
void RipNg::HandleRequests (RipNgHeader requestHdr, Ipv6Address senderAddress, uint16_t senderPort, uint32_t incomingInterface, uint8_t hopLimit)
{
NS_LOG_FUNCTION (this << senderAddress << int (senderPort) << incomingInterface << int (hopLimit) << requestHdr);
std::list<RipNgRte> rtes = requestHdr.GetRteList ();
if (rtes.empty ())
{
return;
}
// check if it's a request for the full table from a neighbor
if (rtes.size () == 1 && senderAddress.IsLinkLocal ())
{
if (rtes.begin ()->GetPrefix () == Ipv6Address::GetAny () &&
rtes.begin ()->GetPrefixLen () == 0 &&
rtes.begin ()->GetRouteMetric () == 16)
{
// 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<Socket> 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_ipv6->GetMtu (incomingInterface);
uint16_t maxRte = (mtu - Ipv6Header ().GetSerializedSize () - UdpHeader ().GetSerializedSize () - RipNgHeader ().GetSerializedSize ()) / RipNgRte ().GetSerializedSize ();
Ptr<Packet> p = Create<Packet> ();
SocketIpv6HopLimitTag tag;
p->RemovePacketTag (tag);
tag.SetHopLimit (255);
p->AddPacketTag (tag);
RipNgHeader hdr;
hdr.SetCommand (RipNgHeader::RESPONSE);
for (RoutesI rtIter = m_routes.begin (); rtIter != m_routes.end (); rtIter++)
{
bool splitHorizoning = (rtIter->first->GetInterface () == incomingInterface);
Ipv6InterfaceAddress rtDestAddr = Ipv6InterfaceAddress(rtIter->first->GetDestNetwork ());
bool isGlobal = (rtDestAddr.GetScope () == Ipv6InterfaceAddress::GLOBAL);
bool isDefaultRoute = ((rtIter->first->GetDestNetwork () == Ipv6Address::GetAny ()) &&
(rtIter->first->GetDestNetworkPrefix () == Ipv6Prefix::GetZero ()) &&
(rtIter->first->GetInterface () != incomingInterface));
if ((isGlobal || isDefaultRoute) &&
(rtIter->first->GetRouteStatus () == RipNgRoutingTableEntry::RIPNG_VALID) )
{
RipNgRte rte;
rte.SetPrefix (rtIter->first->GetDestNetwork ());
rte.SetPrefixLen (rtIter->first->GetDestNetworkPrefix ().GetPrefixLength ());
if (m_splitHorizonStrategy == POISON_REVERSE && splitHorizoning)
{
rte.SetRouteMetric (16);
}
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, Inet6SocketAddress (senderAddress, RIPNG_PORT));
p->RemoveHeader (hdr);
hdr.ClearRtes ();
}
}
if (hdr.GetRteNumber () > 0)
{
p->AddHeader (hdr);
NS_LOG_DEBUG ("SendTo: " << *p);
sendingSoket->SendTo (p, 0, Inet6SocketAddress (senderAddress, RIPNG_PORT));
}
}
}
}
else
{
// note: we got the request as a single packet, so no check is necessary for MTU limit
// 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<Socket> sendingSoket;
if (senderAddress.IsLinkLocal ())
{
for (SocketListI iter = m_sendSocketList.begin (); iter != m_sendSocketList.end (); iter++ )
{
if (iter->second == incomingInterface)
{
sendingSoket = iter->first;
}
}
}
else
{
sendingSoket = m_recvSocket;
}
Ptr<Packet> p = Create<Packet> ();
SocketIpv6HopLimitTag tag;
p->RemovePacketTag (tag);
tag.SetHopLimit (255);
p->AddPacketTag (tag);
RipNgHeader hdr;
hdr.SetCommand (RipNgHeader::RESPONSE);
for (std::list<RipNgRte>::iterator iter = rtes.begin ();
iter != rtes.end (); iter++)
{
bool found = false;
for (RoutesI rtIter = m_routes.begin (); rtIter != m_routes.end (); rtIter++)
{
Ipv6InterfaceAddress rtDestAddr = Ipv6InterfaceAddress(rtIter->first->GetDestNetwork ());
if ((rtDestAddr.GetScope () == Ipv6InterfaceAddress::GLOBAL) &&
(rtIter->first->GetRouteStatus () == RipNgRoutingTableEntry::RIPNG_VALID))
{
Ipv6Address requestedAddress = iter->GetPrefix ();
requestedAddress.CombinePrefix (Ipv6Prefix (iter->GetPrefixLen ()));
Ipv6Address rtAddress = rtIter->first->GetDestNetwork ();
rtAddress.CombinePrefix (rtIter->first->GetDestNetworkPrefix ());
if (requestedAddress == rtAddress)
{
iter->SetRouteMetric (rtIter->first->GetRouteMetric ());
iter->SetRouteTag (rtIter->first->GetRouteTag ());
hdr.AddRte (*iter);
found = true;
break;
}
}
}
if (!found)
{
iter->SetRouteMetric (16);
iter->SetRouteTag (0);
hdr.AddRte (*iter);
}
}
p->AddHeader (hdr);
NS_LOG_DEBUG ("SendTo: " << *p);
sendingSoket->SendTo (p, 0, Inet6SocketAddress (senderAddress, senderPort));
}
}
void RipNg::HandleResponses (RipNgHeader hdr, Ipv6Address 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;
}
if (!senderAddress.IsLinkLocal ())
{
NS_LOG_LOGIC ("Ignoring an update message from a non-link-local source: " << senderAddress);
return;
}
if (hopLimit != 255)
{
NS_LOG_LOGIC ("Ignoring an update message with suspicious hop count: " << int (hopLimit));
return;
}
std::list<RipNgRte> rtes = hdr.GetRteList ();
// validate the RTEs before processing
for (std::list<RipNgRte>::iterator iter = rtes.begin ();
iter != rtes.end (); iter++)
{
if (iter->GetRouteMetric () == 0 || iter->GetRouteMetric () > 16)
{
NS_LOG_LOGIC ("Ignoring an update message with malformed metric: " << int (iter->GetRouteMetric ()));
return;
}
if (iter->GetPrefixLen () > 128)
{
NS_LOG_LOGIC ("Ignoring an update message with malformed prefix length: " << int (iter->GetPrefixLen ()));
return;
}
if (iter->GetPrefix ().IsLocalhost () ||
iter->GetPrefix ().IsLinkLocal () ||
iter->GetPrefix ().IsMulticast ())
{
NS_LOG_LOGIC ("Ignoring an update message with wrong prefixes: " << iter->GetPrefix ());
return;
}
}
bool changed = false;
for (std::list<RipNgRte>::iterator iter = rtes.begin ();
iter != rtes.end (); iter++)
{
Ipv6Prefix rtePrefix = Ipv6Prefix (iter->GetPrefixLen ());
Ipv6Address rteAddr = iter->GetPrefix ().CombinePrefix (rtePrefix);
NS_LOG_LOGIC ("Processing RTE " << *iter);
uint8_t interfaceMetric = 1;
if (m_interfaceMetrics.find (incomingInterface) != m_interfaceMetrics.end ())
{
interfaceMetric = m_interfaceMetrics[incomingInterface];
}
uint8_t rteMetric = std::min (iter->GetRouteMetric () + interfaceMetric, 16);
RoutesI it;
bool found = false;
for (it = m_routes.begin (); it != m_routes.end (); it++)
{
if (it->first->GetDestNetwork () == rteAddr &&
it->first->GetDestNetworkPrefix () == rtePrefix)
{
found = true;
if (rteMetric < it->first->GetRouteMetric ())
{
if (senderAddress != it->first->GetGateway ())
{
RipNgRoutingTableEntry* route = new RipNgRoutingTableEntry (rteAddr, rtePrefix, senderAddress, incomingInterface, Ipv6Address::GetAny ());
delete it->first;
it->first = route;
}
it->first->SetRouteMetric (rteMetric);
it->first->SetRouteStatus (RipNgRoutingTableEntry::RIPNG_VALID);
it->first->SetRouteTag (iter->GetRouteTag ());
it->first->SetRouteChanged (true);
it->second.Cancel ();
it->second = Simulator::Schedule (m_timeoutDelay, &RipNg::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, &RipNg::InvalidateRoute, this, it->first);
}
else
{
if (Simulator::GetDelayLeft (it->second) < m_timeoutDelay/2)
{
RipNgRoutingTableEntry* route = new RipNgRoutingTableEntry (rteAddr, rtePrefix, senderAddress, incomingInterface, Ipv6Address::GetAny ());
route->SetRouteMetric (rteMetric);
route->SetRouteStatus (RipNgRoutingTableEntry::RIPNG_VALID);
route->SetRouteTag (iter->GetRouteTag ());
route->SetRouteChanged (true);
delete it->first;
it->first = route;
it->second.Cancel ();
it->second = Simulator::Schedule (m_timeoutDelay, &RipNg::InvalidateRoute, this, route);
changed = true;
}
}
}
else if (rteMetric > it->first->GetRouteMetric () && senderAddress == it->first->GetGateway ())
{
it->second.Cancel ();
if (rteMetric < 16)
{
it->first->SetRouteMetric (rteMetric);
it->first->SetRouteStatus (RipNgRoutingTableEntry::RIPNG_VALID);
it->first->SetRouteTag (iter->GetRouteTag ());
it->first->SetRouteChanged (true);
it->second.Cancel ();
it->second = Simulator::Schedule (m_timeoutDelay, &RipNg::InvalidateRoute, this, it->first);
}
else
{
InvalidateRoute (it->first);
}
changed = true;
}
}
}
if (!found && rteMetric != 16)
{
NS_LOG_LOGIC ("Received a RTE with new route, adding.");
RipNgRoutingTableEntry* route = new RipNgRoutingTableEntry (rteAddr, rtePrefix, senderAddress, incomingInterface, Ipv6Address::GetAny ());
route->SetRouteMetric (rteMetric);
route->SetRouteStatus (RipNgRoutingTableEntry::RIPNG_VALID);
route->SetRouteChanged (true);
m_routes.push_front (std::make_pair (route, EventId ()));
EventId invalidateEvent = Simulator::Schedule (m_timeoutDelay, &RipNg::InvalidateRoute, this, route);
(m_routes.begin ())->second = invalidateEvent;
changed = true;
}
}
if (changed)
{
SendTriggeredRouteUpdate ();
}
}
void RipNg::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_ipv6->GetMtu (interface);
uint16_t maxRte = (mtu - Ipv6Header ().GetSerializedSize () - UdpHeader ().GetSerializedSize () - RipNgHeader ().GetSerializedSize ()) / RipNgRte ().GetSerializedSize ();
Ptr<Packet> p = Create<Packet> ();
SocketIpv6HopLimitTag tag;
tag.SetHopLimit (255);
p->AddPacketTag (tag);
RipNgHeader hdr;
hdr.SetCommand (RipNgHeader::RESPONSE);
for (RoutesI rtIter = m_routes.begin (); rtIter != m_routes.end (); rtIter++)
{
bool splitHorizoning = (rtIter->first->GetInterface () == interface);
Ipv6InterfaceAddress rtDestAddr = Ipv6InterfaceAddress(rtIter->first->GetDestNetwork ());
NS_LOG_DEBUG ("Processing RT " << rtDestAddr << " " << int(rtIter->first->IsRouteChanged ()));
bool isGlobal = (rtDestAddr.GetScope () == Ipv6InterfaceAddress::GLOBAL);
bool isDefaultRoute = ((rtIter->first->GetDestNetwork () == Ipv6Address::GetAny ()) &&
(rtIter->first->GetDestNetworkPrefix () == Ipv6Prefix::GetZero ()) &&
(rtIter->first->GetInterface () != interface));
if ((isGlobal || isDefaultRoute) &&
(periodic || rtIter->first->IsRouteChanged ()))
{
RipNgRte rte;
rte.SetPrefix (rtIter->first->GetDestNetwork ());
rte.SetPrefixLen (rtIter->first->GetDestNetworkPrefix ().GetPrefixLength ());
if (m_splitHorizonStrategy == POISON_REVERSE && splitHorizoning)
{
rte.SetRouteMetric (16);
}
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, Inet6SocketAddress (RIPNG_ALL_NODE, RIPNG_PORT));
p->RemoveHeader (hdr);
hdr.ClearRtes ();
}
}
if (hdr.GetRteNumber () > 0)
{
p->AddHeader (hdr);
NS_LOG_DEBUG ("SendTo: " << *p);
iter->first->SendTo (p, 0, Inet6SocketAddress (RIPNG_ALL_NODE, RIPNG_PORT));
}
}
}
for (RoutesI rtIter = m_routes.begin (); rtIter != m_routes.end (); rtIter++)
{
rtIter->first->SetRouteChanged (false);
}
}
void RipNg::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, &RipNg::DoSendRouteUpdate, this, false);
}
void RipNg::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, &RipNg::SendUnsolicitedRouteUpdate, this);
}
std::set<uint32_t> RipNg::GetInterfaceExclusions () const
{
return m_interfaceExclusions;
}
void RipNg::SetInterfaceExclusions (std::set<uint32_t> exceptions)
{
NS_LOG_FUNCTION (this);
m_interfaceExclusions = exceptions;
}
uint8_t RipNg::GetInterfaceMetric (uint32_t interface) const
{
NS_LOG_FUNCTION (this << interface);
std::map<uint32_t, uint8_t>::const_iterator iter = m_interfaceMetrics.find (interface);
if (iter != m_interfaceMetrics.end ())
{
return iter->second;
}
return 1;
}
void RipNg::SetInterfaceMetric (uint32_t interface, uint8_t metric)
{
NS_LOG_FUNCTION (this << interface << int (metric));
if (metric < 16)
{
m_interfaceMetrics[interface] = metric;
}
}
void RipNg::SendRouteRequest ()
{
NS_LOG_FUNCTION (this);
Ptr<Packet> p = Create<Packet> ();
SocketIpv6HopLimitTag tag;
p->RemovePacketTag (tag);
tag.SetHopLimit (255);
p->AddPacketTag (tag);
RipNgHeader hdr;
hdr.SetCommand (RipNgHeader::REQUEST);
RipNgRte rte;
rte.SetPrefix (Ipv6Address::GetAny ());
rte.SetPrefixLen (0);
rte.SetRouteMetric (16);
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, Inet6SocketAddress (RIPNG_ALL_NODE, RIPNG_PORT));
}
}
}
void RipNg::AddDefaultRouteTo (Ipv6Address nextHop, uint32_t interface)
{
NS_LOG_FUNCTION (this << interface);
AddNetworkRouteTo (Ipv6Address ("::"), Ipv6Prefix::GetZero (), nextHop, interface, Ipv6Address ("::"));
}
/*
* RipNgRoutingTableEntry
*/
RipNgRoutingTableEntry::RipNgRoutingTableEntry ()
: m_tag (0), m_metric (16), m_status (RIPNG_INVALID), m_changed (false)
{
}
RipNgRoutingTableEntry::RipNgRoutingTableEntry (Ipv6Address network, Ipv6Prefix networkPrefix, Ipv6Address nextHop, uint32_t interface, Ipv6Address prefixToUse)
: Ipv6RoutingTableEntry ( RipNgRoutingTableEntry::CreateNetworkRouteTo (network, networkPrefix, nextHop, interface, prefixToUse) ),
m_tag (0), m_metric (16), m_status (RIPNG_INVALID), m_changed (false)
{
}
RipNgRoutingTableEntry::RipNgRoutingTableEntry (Ipv6Address network, Ipv6Prefix networkPrefix, uint32_t interface)
: Ipv6RoutingTableEntry ( Ipv6RoutingTableEntry::CreateNetworkRouteTo (network, networkPrefix, interface) ),
m_tag (0), m_metric (16), m_status (RIPNG_INVALID), m_changed (false)
{
}
RipNgRoutingTableEntry::~RipNgRoutingTableEntry ()
{
}
void RipNgRoutingTableEntry::SetRouteTag (uint16_t routeTag)
{
if (m_tag != routeTag)
{
m_tag = routeTag;
m_changed = true;
}
}
uint16_t RipNgRoutingTableEntry::GetRouteTag () const
{
return m_tag;
}
void RipNgRoutingTableEntry::SetRouteMetric (uint8_t routeMetric)
{
if (m_metric != routeMetric)
{
m_metric = routeMetric;
m_changed = true;
}
}
uint8_t RipNgRoutingTableEntry::GetRouteMetric () const
{
return m_metric;
}
void RipNgRoutingTableEntry::SetRouteStatus (Status_e status)
{
if (m_status != status)
{
m_status = status;
m_changed = true;
}
}
RipNgRoutingTableEntry::Status_e RipNgRoutingTableEntry::GetRouteStatus (void) const
{
return m_status;
}
void RipNgRoutingTableEntry::SetRouteChanged (bool changed)
{
m_changed = changed;
}
bool RipNgRoutingTableEntry::IsRouteChanged (void) const
{
return m_changed;
}
std::ostream & operator << (std::ostream& os, const RipNgRoutingTableEntry& rte)
{
os << static_cast<const Ipv6RoutingTableEntry &>(rte);
os << ", metric: " << int (rte.GetRouteMetric ()) << ", tag: " << int (rte.GetRouteTag ());
return os;
}
}