435 lines
11 KiB
C++
435 lines
11 KiB
C++
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* Copyright (c) 2006 INRIA
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation;
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
|
|
*/
|
|
#include "ns3/assert.h"
|
|
#include "ns3/packet.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/uinteger.h"
|
|
#include "ns3/log.h"
|
|
#include "ns3/node.h"
|
|
#include "ns3/trace-source-accessor.h"
|
|
|
|
#include "arp-cache.h"
|
|
#include "arp-header.h"
|
|
#include "ipv4-interface.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("ArpCache");
|
|
|
|
namespace ns3 {
|
|
|
|
NS_OBJECT_ENSURE_REGISTERED (ArpCache);
|
|
|
|
TypeId
|
|
ArpCache::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::ArpCache")
|
|
.SetParent<Object> ()
|
|
.AddAttribute ("AliveTimeout",
|
|
"When this timeout expires, the matching cache entry needs refreshing",
|
|
TimeValue (Seconds (120)),
|
|
MakeTimeAccessor (&ArpCache::m_aliveTimeout),
|
|
MakeTimeChecker ())
|
|
.AddAttribute ("DeadTimeout",
|
|
"When this timeout expires, a new attempt to resolve the matching entry is made",
|
|
TimeValue (Seconds (100)),
|
|
MakeTimeAccessor (&ArpCache::m_deadTimeout),
|
|
MakeTimeChecker ())
|
|
.AddAttribute ("WaitReplyTimeout",
|
|
"When this timeout expires, the cache entries will be scanned and entries in WaitReply state will resend ArpRequest unless MaxRetries has been exceeded, in which case the entry is marked dead",
|
|
TimeValue (Seconds (1)),
|
|
MakeTimeAccessor (&ArpCache::m_waitReplyTimeout),
|
|
MakeTimeChecker ())
|
|
.AddAttribute ("MaxRetries",
|
|
"Number of retransmissions of ArpRequest before marking dead",
|
|
UintegerValue (3),
|
|
MakeUintegerAccessor (&ArpCache::m_maxRetries),
|
|
MakeUintegerChecker<uint32_t> ())
|
|
.AddAttribute ("PendingQueueSize",
|
|
"The size of the queue for packets pending an arp reply.",
|
|
UintegerValue (3),
|
|
MakeUintegerAccessor (&ArpCache::m_pendingQueueSize),
|
|
MakeUintegerChecker<uint32_t> ())
|
|
.AddTraceSource ("Drop",
|
|
"Packet dropped due to ArpCache entry in WaitReply expiring.",
|
|
MakeTraceSourceAccessor (&ArpCache::m_dropTrace))
|
|
;
|
|
return tid;
|
|
}
|
|
|
|
ArpCache::ArpCache ()
|
|
: m_device (0),
|
|
m_interface (0)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
}
|
|
|
|
ArpCache::~ArpCache ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
}
|
|
|
|
void
|
|
ArpCache::DoDispose (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
Flush ();
|
|
m_device = 0;
|
|
m_interface = 0;
|
|
if (!m_waitReplyTimer.IsRunning ())
|
|
{
|
|
Simulator::Remove (m_waitReplyTimer);
|
|
}
|
|
Object::DoDispose ();
|
|
}
|
|
|
|
void
|
|
ArpCache::SetDevice (Ptr<NetDevice> device, Ptr<Ipv4Interface> interface)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
m_device = device;
|
|
m_interface = interface;
|
|
}
|
|
|
|
Ptr<NetDevice>
|
|
ArpCache::GetDevice (void) const
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return m_device;
|
|
}
|
|
|
|
Ptr<Ipv4Interface>
|
|
ArpCache::GetInterface (void) const
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return m_interface;
|
|
}
|
|
|
|
void
|
|
ArpCache::SetAliveTimeout (Time aliveTimeout)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
m_aliveTimeout = aliveTimeout;
|
|
}
|
|
void
|
|
ArpCache::SetDeadTimeout (Time deadTimeout)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
m_deadTimeout = deadTimeout;
|
|
}
|
|
void
|
|
ArpCache::SetWaitReplyTimeout (Time waitReplyTimeout)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
m_waitReplyTimeout = waitReplyTimeout;
|
|
}
|
|
|
|
Time
|
|
ArpCache::GetAliveTimeout (void) const
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return m_aliveTimeout;
|
|
}
|
|
Time
|
|
ArpCache::GetDeadTimeout (void) const
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return m_deadTimeout;
|
|
}
|
|
Time
|
|
ArpCache::GetWaitReplyTimeout (void) const
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return m_waitReplyTimeout;
|
|
}
|
|
|
|
void
|
|
ArpCache::SetArpRequestCallback (Callback<void, Ptr<const ArpCache>,
|
|
Ipv4Address> arpRequestCallback)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
m_arpRequestCallback = arpRequestCallback;
|
|
}
|
|
|
|
void
|
|
ArpCache::StartWaitReplyTimer (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
if (!m_waitReplyTimer.IsRunning ())
|
|
{
|
|
NS_LOG_LOGIC ("Starting WaitReplyTimer at " << Simulator::Now ().GetSeconds ());
|
|
m_waitReplyTimer = Simulator::Schedule (m_waitReplyTimeout,
|
|
&ArpCache::HandleWaitReplyTimeout, this);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArpCache::HandleWaitReplyTimeout (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
ArpCache::Entry* entry;
|
|
bool restartWaitReplyTimer = false;
|
|
for (CacheI i = m_arpCache.begin (); i != m_arpCache.end (); i++)
|
|
{
|
|
entry = (*i).second;
|
|
if (entry != 0 && entry->IsWaitReply ())
|
|
{
|
|
if (entry->GetRetries () < m_maxRetries)
|
|
{
|
|
NS_LOG_LOGIC ("node="<< m_device->GetNode ()->GetId () <<
|
|
", ArpWaitTimeout for " << entry->GetIpv4Address () <<
|
|
" expired -- retransmitting arp request since retries = " <<
|
|
entry->GetRetries ());
|
|
m_arpRequestCallback (this, entry->GetIpv4Address ());
|
|
restartWaitReplyTimer = true;
|
|
entry->IncrementRetries ();
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_LOGIC ("node="<<m_device->GetNode ()->GetId () <<
|
|
", wait reply for " << entry->GetIpv4Address () <<
|
|
" expired -- drop since max retries exceeded: " <<
|
|
entry->GetRetries ());
|
|
entry->MarkDead ();
|
|
entry->ClearRetries ();
|
|
Ptr<Packet> pending = entry->DequeuePending();
|
|
while (pending != 0)
|
|
{
|
|
m_dropTrace (pending);
|
|
pending = entry->DequeuePending();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
if (restartWaitReplyTimer)
|
|
{
|
|
NS_LOG_LOGIC ("Restarting WaitReplyTimer at " << Simulator::Now ().GetSeconds ());
|
|
m_waitReplyTimer = Simulator::Schedule (m_waitReplyTimeout,
|
|
&ArpCache::HandleWaitReplyTimeout, this);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArpCache::Flush (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
for (CacheI i = m_arpCache.begin (); i != m_arpCache.end (); i++)
|
|
{
|
|
delete (*i).second;
|
|
}
|
|
m_arpCache.erase (m_arpCache.begin (), m_arpCache.end ());
|
|
if (m_waitReplyTimer.IsRunning ())
|
|
{
|
|
NS_LOG_LOGIC ("Stopping WaitReplyTimer at " << Simulator::Now ().GetSeconds () << " due to ArpCache flush");
|
|
m_waitReplyTimer.Cancel ();
|
|
}
|
|
}
|
|
|
|
ArpCache::Entry *
|
|
ArpCache::Lookup (Ipv4Address to)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
if (m_arpCache.find (to) != m_arpCache.end ())
|
|
{
|
|
ArpCache::Entry *entry = m_arpCache[to];
|
|
return entry;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ArpCache::Entry *
|
|
ArpCache::Add (Ipv4Address to)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
NS_ASSERT (m_arpCache.find (to) == m_arpCache.end ());
|
|
|
|
ArpCache::Entry *entry = new ArpCache::Entry (this);
|
|
m_arpCache[to] = entry;
|
|
entry->SetIpv4Address (to);
|
|
return entry;
|
|
}
|
|
|
|
ArpCache::Entry::Entry (ArpCache *arp)
|
|
: m_arp (arp),
|
|
m_state (ALIVE),
|
|
m_retries (0)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
}
|
|
|
|
|
|
bool
|
|
ArpCache::Entry::IsDead (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return (m_state == DEAD)?true:false;
|
|
}
|
|
bool
|
|
ArpCache::Entry::IsAlive (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return (m_state == ALIVE)?true:false;
|
|
}
|
|
bool
|
|
ArpCache::Entry::IsWaitReply (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return (m_state == WAIT_REPLY)?true:false;
|
|
}
|
|
|
|
|
|
void
|
|
ArpCache::Entry::MarkDead (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
m_state = DEAD;
|
|
ClearRetries ();
|
|
UpdateSeen ();
|
|
}
|
|
void
|
|
ArpCache::Entry::MarkAlive (Address macAddress)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
NS_ASSERT (m_state == WAIT_REPLY);
|
|
m_macAddress = macAddress;
|
|
m_state = ALIVE;
|
|
ClearRetries ();
|
|
UpdateSeen ();
|
|
}
|
|
|
|
bool
|
|
ArpCache::Entry::UpdateWaitReply (Ptr<Packet> waiting)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
NS_ASSERT (m_state == WAIT_REPLY);
|
|
/* We are already waiting for an answer so
|
|
* we dump the previously waiting packet and
|
|
* replace it with this one.
|
|
*/
|
|
if (m_pending.size () >= m_arp->m_pendingQueueSize)
|
|
{
|
|
return false;
|
|
}
|
|
m_pending.push_back (waiting);
|
|
return true;
|
|
}
|
|
void
|
|
ArpCache::Entry::MarkWaitReply (Ptr<Packet> waiting)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
NS_ASSERT (m_state == ALIVE || m_state == DEAD);
|
|
NS_ASSERT (m_pending.empty ());
|
|
m_state = WAIT_REPLY;
|
|
m_pending.push_back (waiting);
|
|
UpdateSeen ();
|
|
m_arp->StartWaitReplyTimer ();
|
|
}
|
|
|
|
Address
|
|
ArpCache::Entry::GetMacAddress (void) const
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
NS_ASSERT (m_state == ALIVE);
|
|
return m_macAddress;
|
|
}
|
|
Ipv4Address
|
|
ArpCache::Entry::GetIpv4Address (void) const
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
return m_ipv4Address;
|
|
}
|
|
void
|
|
ArpCache::Entry::SetIpv4Address (Ipv4Address destination)
|
|
{
|
|
NS_LOG_FUNCTION (this << destination);
|
|
m_ipv4Address = destination;
|
|
}
|
|
Time
|
|
ArpCache::Entry::GetTimeout (void) const
|
|
{
|
|
switch (m_state) {
|
|
case ArpCache::Entry::WAIT_REPLY:
|
|
return m_arp->GetWaitReplyTimeout ();
|
|
case ArpCache::Entry::DEAD:
|
|
return m_arp->GetDeadTimeout ();
|
|
case ArpCache::Entry::ALIVE:
|
|
return m_arp->GetAliveTimeout ();
|
|
default:
|
|
NS_ASSERT (false);
|
|
return Seconds (0);
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
bool
|
|
ArpCache::Entry::IsExpired (void) const
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
Time timeout = GetTimeout ();
|
|
Time delta = Simulator::Now () - m_lastSeen;
|
|
NS_LOG_DEBUG ("delta=" << delta.GetSeconds () << "s");
|
|
if (delta > timeout)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
Ptr<Packet>
|
|
ArpCache::Entry::DequeuePending (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
if (m_pending.empty ())
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
Ptr<Packet> p = m_pending.front ();
|
|
m_pending.pop_front ();
|
|
return p;
|
|
}
|
|
}
|
|
void
|
|
ArpCache::Entry::UpdateSeen (void)
|
|
{
|
|
NS_LOG_FUNCTION (m_macAddress << m_ipv4Address);
|
|
m_lastSeen = Simulator::Now ();
|
|
}
|
|
uint32_t
|
|
ArpCache::Entry::GetRetries (void) const
|
|
{
|
|
NS_LOG_FUNCTION (m_macAddress << m_ipv4Address);
|
|
return m_retries;
|
|
}
|
|
void
|
|
ArpCache::Entry::IncrementRetries (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
m_retries++;
|
|
UpdateSeen ();
|
|
}
|
|
void
|
|
ArpCache::Entry::ClearRetries (void)
|
|
{
|
|
NS_LOG_FUNCTION_NOARGS ();
|
|
m_retries = 0;
|
|
}
|
|
|
|
} // namespace ns3
|
|
|