Files
unison/src/internet/model/ndisc-cache.cc
2025-09-28 09:33:53 +00:00

743 lines
16 KiB
C++

/*
* Copyright (c) 2007-2009 Strasbourg University
*
* SPDX-License-Identifier: GPL-2.0-only
*
* Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
*/
#include "ndisc-cache.h"
#include "icmpv6-l4-protocol.h"
#include "ipv6-interface.h"
#include "ipv6-l3-protocol.h"
#include "ns3/log.h"
#include "ns3/names.h"
#include "ns3/node.h"
#include "ns3/uinteger.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("NdiscCache");
NS_OBJECT_ENSURE_REGISTERED(NdiscCache);
TypeId
NdiscCache::GetTypeId()
{
static TypeId tid = TypeId("ns3::NdiscCache")
.SetParent<Object>()
.SetGroupName("Internet")
.AddAttribute("UnresolvedQueueSize",
"Size of the queue for packets pending an NA reply.",
UintegerValue(DEFAULT_UNRES_QLEN),
MakeUintegerAccessor(&NdiscCache::m_unresQlen),
MakeUintegerChecker<uint32_t>());
return tid;
}
NdiscCache::NdiscCache()
{
NS_LOG_FUNCTION(this);
}
NdiscCache::~NdiscCache()
{
NS_LOG_FUNCTION(this);
Flush();
}
void
NdiscCache::DoDispose()
{
NS_LOG_FUNCTION(this);
for (auto& iter : m_ndCache)
{
delete iter.second; /* delete the pointer NdiscCache::Entry */
}
m_ndCache.clear();
m_device = nullptr;
m_interface = nullptr;
m_icmpv6 = nullptr;
Object::DoDispose();
}
void
NdiscCache::SetDevice(Ptr<NetDevice> device,
Ptr<Ipv6Interface> interface,
Ptr<Icmpv6L4Protocol> icmpv6)
{
NS_LOG_FUNCTION(this << device << interface);
m_device = device;
m_interface = interface;
m_icmpv6 = icmpv6;
}
Ptr<Ipv6Interface>
NdiscCache::GetInterface() const
{
NS_LOG_FUNCTION(this);
return m_interface;
}
Ptr<NetDevice>
NdiscCache::GetDevice() const
{
NS_LOG_FUNCTION(this);
return m_device;
}
NdiscCache::Entry*
NdiscCache::Lookup(Ipv6Address dst)
{
NS_LOG_FUNCTION(this << dst);
if (m_ndCache.find(dst) != m_ndCache.end())
{
NdiscCache::Entry* entry = m_ndCache[dst];
NS_LOG_LOGIC("Found an entry: " << *entry);
return entry;
}
NS_LOG_LOGIC("Nothing found");
return nullptr;
}
std::list<NdiscCache::Entry*>
NdiscCache::LookupInverse(Address dst)
{
NS_LOG_FUNCTION(this << dst);
std::list<NdiscCache::Entry*> entryList;
for (auto i = m_ndCache.begin(); i != m_ndCache.end(); i++)
{
NdiscCache::Entry* entry = (*i).second;
if (entry->GetMacAddress() == dst)
{
NS_LOG_LOGIC("Found an entry:" << (*entry));
entryList.push_back(entry);
}
}
return entryList;
}
NdiscCache::Entry*
NdiscCache::Add(Ipv6Address to)
{
NS_LOG_FUNCTION(this << to);
NS_ASSERT(m_ndCache.find(to) == m_ndCache.end());
auto entry = new NdiscCache::Entry(this);
entry->SetIpv6Address(to);
m_ndCache[to] = entry;
return entry;
}
void
NdiscCache::Remove(NdiscCache::Entry* entry)
{
NS_LOG_FUNCTION(this << entry);
for (auto i = m_ndCache.begin(); i != m_ndCache.end(); i++)
{
if ((*i).second == entry)
{
m_ndCache.erase(i);
entry->ClearWaitingPacket();
delete entry;
return;
}
}
}
void
NdiscCache::Flush()
{
NS_LOG_FUNCTION(this);
for (auto i = m_ndCache.begin(); i != m_ndCache.end();)
{
if (!i->second->IsAutoGenerated())
{
i->second->ClearWaitingPacket();
delete i->second;
i = m_ndCache.erase(i);
}
else
{
i++;
}
}
}
void
NdiscCache::SetUnresQlen(uint32_t unresQlen)
{
NS_LOG_FUNCTION(this << unresQlen);
m_unresQlen = unresQlen;
}
uint32_t
NdiscCache::GetUnresQlen()
{
NS_LOG_FUNCTION(this);
return m_unresQlen;
}
void
NdiscCache::PrintNdiscCache(Ptr<OutputStreamWrapper> stream)
{
NS_LOG_FUNCTION(this << stream);
std::ostream* os = stream->GetStream();
for (auto i = m_ndCache.begin(); i != m_ndCache.end(); i++)
{
*os << i->first << " dev ";
std::string found = Names::FindName(m_device);
if (!Names::FindName(m_device).empty())
{
*os << found;
}
else
{
*os << static_cast<int>(m_device->GetIfIndex());
}
*os << " lladdr " << i->second->GetMacAddress();
if (i->second->IsReachable())
{
*os << " REACHABLE\n";
}
else if (i->second->IsDelay())
{
*os << " DELAY\n";
}
else if (i->second->IsIncomplete())
{
*os << " INCOMPLETE\n";
}
else if (i->second->IsProbe())
{
*os << " PROBE\n";
}
else if (i->second->IsStale())
{
*os << " STALE\n";
}
else if (i->second->IsPermanent())
{
*os << " PERMANENT\n";
}
else if (i->second->IsAutoGenerated())
{
*os << " STATIC_AUTOGENERATED\n";
}
else
{
NS_FATAL_ERROR("Test for possibly unreachable code-- please file a bug report, with a "
"test case, if this is ever hit");
}
}
}
NdiscCache::Entry::Entry(NdiscCache* nd)
: m_ndCache(nd),
m_waiting(),
m_router(false),
m_nudTimer(Timer::CANCEL_ON_DESTROY),
m_lastReachabilityConfirmation(),
m_nsRetransmit(0)
{
NS_LOG_FUNCTION(this);
}
void
NdiscCache::Entry::SetRouter(bool router)
{
NS_LOG_FUNCTION(this << router);
m_router = router;
}
bool
NdiscCache::Entry::IsRouter() const
{
NS_LOG_FUNCTION(this);
return m_router;
}
void
NdiscCache::Entry::AddWaitingPacket(Ipv6PayloadHeaderPair p)
{
NS_LOG_FUNCTION(this << p.second << p.first);
if (m_waiting.size() >= m_ndCache->GetUnresQlen())
{
/* we store only m_unresQlen packet => first packet in first packet remove */
/** @todo report packet as 'dropped' */
m_waiting.pop_front();
}
m_waiting.push_back(p);
}
void
NdiscCache::Entry::ClearWaitingPacket()
{
NS_LOG_FUNCTION(this);
/** @todo report packets as 'dropped' */
m_waiting.clear();
}
void
NdiscCache::Entry::FunctionReachableTimeout()
{
NS_LOG_FUNCTION(this);
this->MarkStale();
}
void
NdiscCache::Entry::FunctionRetransmitTimeout()
{
NS_LOG_FUNCTION(this);
Ipv6Address addr;
/* determine source address */
if (m_ipv6Address.IsLinkLocal())
{
addr = m_ndCache->GetInterface()->GetLinkLocalAddress().GetAddress();
}
else if (!m_ipv6Address.IsAny())
{
addr = m_ndCache->GetInterface()->GetAddressMatchingDestination(m_ipv6Address).GetAddress();
if (addr.IsAny()) /* maybe address has expired */
{
/* delete the entry */
m_ndCache->Remove(this);
return;
}
}
if (m_nsRetransmit < m_ndCache->m_icmpv6->GetMaxMulticastSolicit())
{
m_nsRetransmit++;
m_ndCache->m_icmpv6->SendNS(addr,
Ipv6Address::MakeSolicitedAddress(m_ipv6Address),
m_ipv6Address,
m_ndCache->GetDevice()->GetAddress());
/* arm the timer again */
StartRetransmitTimer();
}
else
{
Ipv6PayloadHeaderPair malformedPacket = m_waiting.front();
if (!malformedPacket.first)
{
malformedPacket.first = Create<Packet>();
}
else
{
malformedPacket.first->AddHeader(malformedPacket.second);
}
m_ndCache->m_icmpv6->SendErrorDestinationUnreachable(malformedPacket.first,
addr,
Icmpv6Header::ICMPV6_ADDR_UNREACHABLE);
/* delete the entry */
m_ndCache->Remove(this);
}
}
void
NdiscCache::Entry::FunctionDelayTimeout()
{
NS_LOG_FUNCTION(this);
Ipv6Address addr;
this->MarkProbe();
if (m_ipv6Address.IsLinkLocal())
{
addr = m_ndCache->GetInterface()->GetLinkLocalAddress().GetAddress();
}
else if (!m_ipv6Address.IsAny())
{
addr = m_ndCache->GetInterface()->GetAddressMatchingDestination(m_ipv6Address).GetAddress();
if (addr.IsAny()) /* maybe address has expired */
{
/* delete the entry */
m_ndCache->Remove(this);
return;
}
}
else
{
/* should not happen */
return;
}
Ipv6PayloadHeaderPair p = m_ndCache->m_icmpv6->ForgeNS(addr,
m_ipv6Address,
m_ipv6Address,
m_ndCache->GetDevice()->GetAddress());
p.first->AddHeader(p.second);
m_ndCache->GetDevice()->Send(p.first, this->GetMacAddress(), Ipv6L3Protocol::PROT_NUMBER);
m_nsRetransmit = 1;
StartProbeTimer();
}
void
NdiscCache::Entry::FunctionProbeTimeout()
{
NS_LOG_FUNCTION(this);
if (m_nsRetransmit < m_ndCache->m_icmpv6->GetMaxUnicastSolicit())
{
m_nsRetransmit++;
Ipv6Address addr;
if (m_ipv6Address.IsLinkLocal())
{
addr = m_ndCache->GetInterface()->GetLinkLocalAddress().GetAddress();
}
else if (!m_ipv6Address.IsAny())
{
addr = m_ndCache->GetInterface()
->GetAddressMatchingDestination(m_ipv6Address)
.GetAddress();
if (addr.IsAny()) /* maybe address has expired */
{
/* delete the entry */
m_ndCache->Remove(this);
return;
}
}
else
{
/* should not happen */
return;
}
/* icmpv6->SendNS (m_ndCache->GetInterface ()->GetLinkLocalAddress (), m_ipv6Address,
* m_ipv6Address, m_ndCache->GetDevice ()->GetAddress ()); */
Ipv6PayloadHeaderPair p =
m_ndCache->m_icmpv6->ForgeNS(addr,
m_ipv6Address,
m_ipv6Address,
m_ndCache->GetDevice()->GetAddress());
p.first->AddHeader(p.second);
m_ndCache->GetDevice()->Send(p.first, this->GetMacAddress(), Ipv6L3Protocol::PROT_NUMBER);
/* arm the timer again */
StartProbeTimer();
}
else
{
/* delete the entry */
m_ndCache->Remove(this);
}
}
void
NdiscCache::Entry::SetIpv6Address(Ipv6Address ipv6Address)
{
NS_LOG_FUNCTION(this << ipv6Address);
m_ipv6Address = ipv6Address;
}
Ipv6Address
NdiscCache::Entry::GetIpv6Address() const
{
NS_LOG_FUNCTION(this);
return m_ipv6Address;
}
Time
NdiscCache::Entry::GetLastReachabilityConfirmation() const
{
NS_LOG_FUNCTION(this);
return m_lastReachabilityConfirmation;
}
void
NdiscCache::Entry::StartReachableTimer()
{
NS_LOG_FUNCTION(this);
if (m_nudTimer.IsRunning())
{
m_nudTimer.Cancel();
}
m_lastReachabilityConfirmation = Simulator::Now();
m_nudTimer.SetFunction(&NdiscCache::Entry::FunctionReachableTimeout, this);
m_nudTimer.SetDelay(m_ndCache->m_icmpv6->GetReachableTime());
m_nudTimer.Schedule();
}
void
NdiscCache::Entry::UpdateReachableTimer()
{
NS_LOG_FUNCTION(this);
if (m_state == REACHABLE)
{
m_lastReachabilityConfirmation = Simulator::Now();
if (m_nudTimer.IsRunning())
{
m_nudTimer.Cancel();
}
m_nudTimer.Schedule();
}
}
void
NdiscCache::Entry::StartProbeTimer()
{
NS_LOG_FUNCTION(this);
if (m_nudTimer.IsRunning())
{
m_nudTimer.Cancel();
}
m_nudTimer.SetFunction(&NdiscCache::Entry::FunctionProbeTimeout, this);
m_nudTimer.SetDelay(m_ndCache->m_icmpv6->GetRetransmissionTime());
m_nudTimer.Schedule();
}
void
NdiscCache::Entry::StartDelayTimer()
{
NS_LOG_FUNCTION(this);
if (m_nudTimer.IsRunning())
{
m_nudTimer.Cancel();
}
m_nudTimer.SetFunction(&NdiscCache::Entry::FunctionDelayTimeout, this);
m_nudTimer.SetDelay(m_ndCache->m_icmpv6->GetDelayFirstProbe());
m_nudTimer.Schedule();
}
void
NdiscCache::Entry::StartRetransmitTimer()
{
NS_LOG_FUNCTION(this);
if (m_nudTimer.IsRunning())
{
m_nudTimer.Cancel();
}
m_nudTimer.SetFunction(&NdiscCache::Entry::FunctionRetransmitTimeout, this);
m_nudTimer.SetDelay(m_ndCache->m_icmpv6->GetRetransmissionTime());
m_nudTimer.Schedule();
}
void
NdiscCache::Entry::StopNudTimer()
{
NS_LOG_FUNCTION(this);
m_nudTimer.Cancel();
m_nsRetransmit = 0;
}
void
NdiscCache::Entry::MarkIncomplete(Ipv6PayloadHeaderPair p)
{
NS_LOG_FUNCTION(this << p.second << p.first);
m_state = INCOMPLETE;
if (p.first)
{
m_waiting.push_back(p);
}
}
std::list<NdiscCache::Ipv6PayloadHeaderPair>
NdiscCache::Entry::MarkReachable(Address mac)
{
NS_LOG_FUNCTION(this << mac);
m_state = REACHABLE;
m_macAddress = mac;
return m_waiting;
}
void
NdiscCache::Entry::MarkProbe()
{
NS_LOG_FUNCTION(this);
m_state = PROBE;
}
void
NdiscCache::Entry::MarkStale()
{
NS_LOG_FUNCTION(this);
m_state = STALE;
}
void
NdiscCache::Entry::MarkReachable()
{
NS_LOG_FUNCTION(this);
m_state = REACHABLE;
}
std::list<NdiscCache::Ipv6PayloadHeaderPair>
NdiscCache::Entry::MarkStale(Address mac)
{
NS_LOG_FUNCTION(this << mac);
m_state = STALE;
m_macAddress = mac;
return m_waiting;
}
void
NdiscCache::Entry::MarkDelay()
{
NS_LOG_FUNCTION(this);
m_state = DELAY;
}
void
NdiscCache::Entry::MarkPermanent()
{
NS_LOG_FUNCTION(this);
StopNudTimer();
m_state = PERMANENT;
}
void
NdiscCache::Entry::MarkAutoGenerated()
{
NS_LOG_FUNCTION(this);
StopNudTimer();
m_state = STATIC_AUTOGENERATED;
}
bool
NdiscCache::Entry::IsStale() const
{
NS_LOG_FUNCTION(this);
return (m_state == STALE);
}
bool
NdiscCache::Entry::IsReachable() const
{
NS_LOG_FUNCTION(this);
return (m_state == REACHABLE);
}
bool
NdiscCache::Entry::IsDelay() const
{
NS_LOG_FUNCTION(this);
return (m_state == DELAY);
}
bool
NdiscCache::Entry::IsIncomplete() const
{
NS_LOG_FUNCTION(this);
return (m_state == INCOMPLETE);
}
bool
NdiscCache::Entry::IsProbe() const
{
NS_LOG_FUNCTION(this);
return (m_state == PROBE);
}
bool
NdiscCache::Entry::IsPermanent() const
{
NS_LOG_FUNCTION(this);
return (m_state == PERMANENT);
}
bool
NdiscCache::Entry::IsAutoGenerated() const
{
NS_LOG_FUNCTION(this);
return (m_state == STATIC_AUTOGENERATED);
}
Address
NdiscCache::Entry::GetMacAddress() const
{
NS_LOG_FUNCTION(this);
return m_macAddress;
}
void
NdiscCache::Entry::SetMacAddress(Address mac)
{
NS_LOG_FUNCTION(this << mac << int(m_state));
m_macAddress = mac;
}
void
NdiscCache::Entry::Print(std::ostream& os) const
{
os << m_ipv6Address << " lladdr " << m_macAddress << " state ";
switch (m_state)
{
case INCOMPLETE:
os << "INCOMPLETE";
break;
case REACHABLE:
os << "REACHABLE";
break;
case STALE:
os << "STALE";
break;
case DELAY:
os << "DELAY";
break;
case PROBE:
os << "PROBE";
break;
case PERMANENT:
os << "PERMANENT";
break;
case STATIC_AUTOGENERATED:
os << "STATIC_AUTOGENERATED";
break;
}
}
void
NdiscCache::RemoveAutoGeneratedEntries()
{
NS_LOG_FUNCTION(this);
for (auto i = m_ndCache.begin(); i != m_ndCache.end();)
{
if (i->second->IsAutoGenerated())
{
i->second->ClearWaitingPacket();
delete i->second;
i = m_ndCache.erase(i);
}
else
{
i++;
}
}
}
std::ostream&
operator<<(std::ostream& os, const NdiscCache::Entry& entry)
{
entry.Print(os);
return os;
}
} /* namespace ns3 */