Files
unison/src/internet/model/ipv4-l3-protocol.cc
2025-02-11 11:45:03 +00:00

1884 lines
57 KiB
C++

//
// Copyright (c) 2006 Georgia Tech Research Corporation
//
// SPDX-License-Identifier: GPL-2.0-only
//
// Author: George F. Riley<riley@ece.gatech.edu>
//
#include "ipv4-l3-protocol.h"
#include "arp-cache.h"
#include "arp-l3-protocol.h"
#include "icmpv4-l4-protocol.h"
#include "ipv4-header.h"
#include "ipv4-interface.h"
#include "ipv4-raw-socket-impl.h"
#include "ipv4-route.h"
#include "loopback-net-device.h"
#include "ns3/boolean.h"
#include "ns3/callback.h"
#include "ns3/ipv4-address.h"
#include "ns3/log.h"
#include "ns3/net-device.h"
#include "ns3/node.h"
#include "ns3/object-vector.h"
#include "ns3/packet.h"
#include "ns3/socket.h"
#include "ns3/string.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/traffic-control-layer.h"
#include "ns3/uinteger.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("Ipv4L3Protocol");
NS_OBJECT_ENSURE_REGISTERED(Ipv4L3Protocol);
TypeId
Ipv4L3Protocol::GetTypeId()
{
static TypeId tid =
TypeId("ns3::Ipv4L3Protocol")
.SetParent<Ipv4>()
.SetGroupName("Internet")
.AddConstructor<Ipv4L3Protocol>()
.AddAttribute("DefaultTtl",
"The TTL value set by default on "
"all outgoing packets generated on this node.",
UintegerValue(64),
MakeUintegerAccessor(&Ipv4L3Protocol::m_defaultTtl),
MakeUintegerChecker<uint8_t>())
.AddAttribute("FragmentExpirationTimeout",
"When this timeout expires, the fragments "
"will be cleared from the buffer.",
TimeValue(Seconds(30)),
MakeTimeAccessor(&Ipv4L3Protocol::m_fragmentExpirationTimeout),
MakeTimeChecker())
.AddAttribute("EnableDuplicatePacketDetection",
"Enable multicast duplicate packet detection based on RFC 6621",
BooleanValue(false),
MakeBooleanAccessor(&Ipv4L3Protocol::m_enableDpd),
MakeBooleanChecker())
.AddAttribute("DuplicateExpire",
"Expiration delay for duplicate cache entries",
TimeValue(MilliSeconds(1)),
MakeTimeAccessor(&Ipv4L3Protocol::m_expire),
MakeTimeChecker())
.AddAttribute("PurgeExpiredPeriod",
"Time between purges of expired duplicate packet entries, "
"0 means never purge",
TimeValue(Seconds(1)),
MakeTimeAccessor(&Ipv4L3Protocol::m_purge),
MakeTimeChecker(Seconds(0)))
.AddTraceSource("Tx",
"Send ipv4 packet to outgoing interface.",
MakeTraceSourceAccessor(&Ipv4L3Protocol::m_txTrace),
"ns3::Ipv4L3Protocol::TxRxTracedCallback")
.AddTraceSource("Rx",
"Receive ipv4 packet from incoming interface.",
MakeTraceSourceAccessor(&Ipv4L3Protocol::m_rxTrace),
"ns3::Ipv4L3Protocol::TxRxTracedCallback")
.AddTraceSource("Drop",
"Drop ipv4 packet",
MakeTraceSourceAccessor(&Ipv4L3Protocol::m_dropTrace),
"ns3::Ipv4L3Protocol::DropTracedCallback")
.AddAttribute("InterfaceList",
"The set of Ipv4 interfaces associated to this Ipv4 stack.",
ObjectVectorValue(),
MakeObjectVectorAccessor(&Ipv4L3Protocol::m_interfaces),
MakeObjectVectorChecker<Ipv4Interface>())
.AddTraceSource("SendOutgoing",
"A newly-generated packet by this node is "
"about to be queued for transmission",
MakeTraceSourceAccessor(&Ipv4L3Protocol::m_sendOutgoingTrace),
"ns3::Ipv4L3Protocol::SentTracedCallback")
.AddTraceSource("UnicastForward",
"A unicast IPv4 packet was received by this node "
"and is being forwarded to another node",
MakeTraceSourceAccessor(&Ipv4L3Protocol::m_unicastForwardTrace),
"ns3::Ipv4L3Protocol::SentTracedCallback")
.AddTraceSource("MulticastForward",
"A multicast IPv4 packet was received by this node "
"and is being forwarded to another node",
MakeTraceSourceAccessor(&Ipv4L3Protocol::m_multicastForwardTrace),
"ns3::Ipv4L3Protocol::SentTracedCallback")
.AddTraceSource("LocalDeliver",
"An IPv4 packet was received by/for this node, "
"and it is being forward up the stack",
MakeTraceSourceAccessor(&Ipv4L3Protocol::m_localDeliverTrace),
"ns3::Ipv4L3Protocol::SentTracedCallback")
;
return tid;
}
Ipv4L3Protocol::Ipv4L3Protocol()
{
NS_LOG_FUNCTION(this);
m_ucb = MakeCallback(&Ipv4L3Protocol::IpForward, this);
m_mcb = MakeCallback(&Ipv4L3Protocol::IpMulticastForward, this);
m_lcb = MakeCallback(&Ipv4L3Protocol::LocalDeliver, this);
m_ecb = MakeCallback(&Ipv4L3Protocol::RouteInputError, this);
}
Ipv4L3Protocol::~Ipv4L3Protocol()
{
NS_LOG_FUNCTION(this);
}
void
Ipv4L3Protocol::Insert(Ptr<IpL4Protocol> protocol)
{
NS_LOG_FUNCTION(this << protocol);
L4ListKey_t key = std::make_pair(protocol->GetProtocolNumber(), -1);
if (m_protocols.find(key) != m_protocols.end())
{
NS_LOG_WARN("Overwriting default protocol " << int(protocol->GetProtocolNumber()));
}
m_protocols[key] = protocol;
}
void
Ipv4L3Protocol::Insert(Ptr<IpL4Protocol> protocol, uint32_t interfaceIndex)
{
NS_LOG_FUNCTION(this << protocol << interfaceIndex);
L4ListKey_t key = std::make_pair(protocol->GetProtocolNumber(), interfaceIndex);
if (m_protocols.find(key) != m_protocols.end())
{
NS_LOG_WARN("Overwriting protocol " << int(protocol->GetProtocolNumber())
<< " on interface " << int(interfaceIndex));
}
m_protocols[key] = protocol;
}
void
Ipv4L3Protocol::Remove(Ptr<IpL4Protocol> protocol)
{
NS_LOG_FUNCTION(this << protocol);
L4ListKey_t key = std::make_pair(protocol->GetProtocolNumber(), -1);
auto iter = m_protocols.find(key);
if (iter == m_protocols.end())
{
NS_LOG_WARN("Trying to remove an non-existent default protocol "
<< int(protocol->GetProtocolNumber()));
}
else
{
m_protocols.erase(key);
}
}
void
Ipv4L3Protocol::Remove(Ptr<IpL4Protocol> protocol, uint32_t interfaceIndex)
{
NS_LOG_FUNCTION(this << protocol << interfaceIndex);
L4ListKey_t key = std::make_pair(protocol->GetProtocolNumber(), interfaceIndex);
auto iter = m_protocols.find(key);
if (iter == m_protocols.end())
{
NS_LOG_WARN("Trying to remove an non-existent protocol "
<< int(protocol->GetProtocolNumber()) << " on interface "
<< int(interfaceIndex));
}
else
{
m_protocols.erase(key);
}
}
Ptr<IpL4Protocol>
Ipv4L3Protocol::GetProtocol(int protocolNumber) const
{
return GetProtocol(protocolNumber, -1);
}
Ptr<IpL4Protocol>
Ipv4L3Protocol::GetProtocol(int protocolNumber, int32_t interfaceIndex) const
{
if (interfaceIndex >= 0)
{
// try the interface-specific protocol.
auto key = std::make_pair(protocolNumber, interfaceIndex);
auto i = m_protocols.find(key);
if (i != m_protocols.end())
{
return i->second;
}
}
// try the generic protocol.
auto key = std::make_pair(protocolNumber, -1);
auto i = m_protocols.find(key);
if (i != m_protocols.end())
{
return i->second;
}
return nullptr;
}
void
Ipv4L3Protocol::SetNode(Ptr<Node> node)
{
NS_LOG_FUNCTION(this << node);
m_node = node;
// Add a LoopbackNetDevice if needed, and an Ipv4Interface on top of it
SetupLoopback();
}
Ptr<Socket>
Ipv4L3Protocol::CreateRawSocket()
{
NS_LOG_FUNCTION(this);
Ptr<Ipv4RawSocketImpl> socket = CreateObject<Ipv4RawSocketImpl>();
socket->SetNode(m_node);
m_sockets.push_back(socket);
return socket;
}
void
Ipv4L3Protocol::DeleteRawSocket(Ptr<Socket> socket)
{
NS_LOG_FUNCTION(this << socket);
for (auto i = m_sockets.begin(); i != m_sockets.end(); ++i)
{
if ((*i) == socket)
{
m_sockets.erase(i);
return;
}
}
}
/*
* This method is called by AggregateObject and completes the aggregation
* by setting the node in the ipv4 stack
*/
void
Ipv4L3Protocol::NotifyNewAggregate()
{
NS_LOG_FUNCTION(this);
if (!m_node)
{
Ptr<Node> node = this->GetObject<Node>();
// verify that it's a valid node and that
// the node has not been set before
if (node)
{
this->SetNode(node);
}
}
Ipv4::NotifyNewAggregate();
}
void
Ipv4L3Protocol::SetRoutingProtocol(Ptr<Ipv4RoutingProtocol> routingProtocol)
{
NS_LOG_FUNCTION(this << routingProtocol);
m_routingProtocol = routingProtocol;
m_routingProtocol->SetIpv4(this);
}
Ptr<Ipv4RoutingProtocol>
Ipv4L3Protocol::GetRoutingProtocol() const
{
return m_routingProtocol;
}
void
Ipv4L3Protocol::DoDispose()
{
NS_LOG_FUNCTION(this);
for (auto i = m_protocols.begin(); i != m_protocols.end(); ++i)
{
i->second = nullptr;
}
m_protocols.clear();
for (auto i = m_interfaces.begin(); i != m_interfaces.end(); ++i)
{
*i = nullptr;
}
m_interfaces.clear();
m_reverseInterfacesContainer.clear();
m_sockets.clear();
m_node = nullptr;
m_routingProtocol = nullptr;
for (auto it = m_fragments.begin(); it != m_fragments.end(); it++)
{
it->second = nullptr;
}
m_fragments.clear();
m_timeoutEventList.clear();
if (m_timeoutEvent.IsPending())
{
m_timeoutEvent.Cancel();
}
if (m_cleanDpd.IsPending())
{
m_cleanDpd.Cancel();
}
m_dups.clear();
Object::DoDispose();
}
void
Ipv4L3Protocol::SetupLoopback()
{
NS_LOG_FUNCTION(this);
Ptr<Ipv4Interface> interface = CreateObject<Ipv4Interface>();
Ptr<LoopbackNetDevice> device = nullptr;
// First check whether an existing LoopbackNetDevice exists on the node
for (uint32_t i = 0; i < m_node->GetNDevices(); i++)
{
if ((device = DynamicCast<LoopbackNetDevice>(m_node->GetDevice(i))))
{
break;
}
}
if (!device)
{
device = CreateObject<LoopbackNetDevice>();
m_node->AddDevice(device);
}
interface->SetDevice(device);
interface->SetNode(m_node);
Ipv4InterfaceAddress ifaceAddr =
Ipv4InterfaceAddress(Ipv4Address::GetLoopback(), Ipv4Mask::GetLoopback());
interface->AddAddress(ifaceAddr);
uint32_t index = AddIpv4Interface(interface);
Ptr<Node> node = GetObject<Node>();
node->RegisterProtocolHandler(MakeCallback(&Ipv4L3Protocol::Receive, this),
Ipv4L3Protocol::PROT_NUMBER,
device);
interface->SetUp();
if (m_routingProtocol)
{
m_routingProtocol->NotifyInterfaceUp(index);
}
}
void
Ipv4L3Protocol::SetDefaultTtl(uint8_t ttl)
{
NS_LOG_FUNCTION(this << static_cast<uint32_t>(ttl));
m_defaultTtl = ttl;
}
uint32_t
Ipv4L3Protocol::AddInterface(Ptr<NetDevice> device)
{
NS_LOG_FUNCTION(this << device);
NS_ASSERT(m_node);
Ptr<TrafficControlLayer> tc = m_node->GetObject<TrafficControlLayer>();
NS_ASSERT(tc);
m_node->RegisterProtocolHandler(MakeCallback(&TrafficControlLayer::Receive, tc),
Ipv4L3Protocol::PROT_NUMBER,
device);
m_node->RegisterProtocolHandler(MakeCallback(&TrafficControlLayer::Receive, tc),
ArpL3Protocol::PROT_NUMBER,
device);
tc->RegisterProtocolHandler(MakeCallback(&Ipv4L3Protocol::Receive, this),
Ipv4L3Protocol::PROT_NUMBER,
device);
tc->RegisterProtocolHandler(
MakeCallback(&ArpL3Protocol::Receive, PeekPointer(GetObject<ArpL3Protocol>())),
ArpL3Protocol::PROT_NUMBER,
device);
Ptr<Ipv4Interface> interface = CreateObject<Ipv4Interface>();
interface->SetNode(m_node);
interface->SetDevice(device);
interface->SetTrafficControl(tc);
interface->SetForwarding(m_ipForward);
return AddIpv4Interface(interface);
}
uint32_t
Ipv4L3Protocol::AddIpv4Interface(Ptr<Ipv4Interface> interface)
{
NS_LOG_FUNCTION(this << interface);
uint32_t index = m_interfaces.size();
m_interfaces.push_back(interface);
m_reverseInterfacesContainer[interface->GetDevice()] = index;
return index;
}
Ptr<Ipv4Interface>
Ipv4L3Protocol::GetInterface(uint32_t index) const
{
if (index < m_interfaces.size())
{
return m_interfaces[index];
}
return nullptr;
}
uint32_t
Ipv4L3Protocol::GetNInterfaces() const
{
return m_interfaces.size();
}
int32_t
Ipv4L3Protocol::GetInterfaceForAddress(Ipv4Address address) const
{
int32_t interface = 0;
for (auto i = m_interfaces.begin(); i != m_interfaces.end(); i++, interface++)
{
for (uint32_t j = 0; j < (*i)->GetNAddresses(); j++)
{
if ((*i)->GetAddress(j).GetLocal() == address)
{
return interface;
}
}
}
return -1;
}
int32_t
Ipv4L3Protocol::GetInterfaceForPrefix(Ipv4Address address, Ipv4Mask mask) const
{
int32_t interface = 0;
for (auto i = m_interfaces.begin(); i != m_interfaces.end(); i++, interface++)
{
for (uint32_t j = 0; j < (*i)->GetNAddresses(); j++)
{
if ((*i)->GetAddress(j).GetLocal().CombineMask(mask) == address.CombineMask(mask))
{
return interface;
}
}
}
return -1;
}
int32_t
Ipv4L3Protocol::GetInterfaceForDevice(Ptr<const NetDevice> device) const
{
auto iter = m_reverseInterfacesContainer.find(device);
if (iter != m_reverseInterfacesContainer.end())
{
return (*iter).second;
}
return -1;
}
bool
Ipv4L3Protocol::IsDestinationAddress(Ipv4Address address, uint32_t iif) const
{
// First check the incoming interface for a unicast address match
for (uint32_t i = 0; i < GetNAddresses(iif); i++)
{
Ipv4InterfaceAddress iaddr = GetAddress(iif, i);
if (address == iaddr.GetLocal())
{
NS_LOG_LOGIC("For me (destination " << address << " match)");
return true;
}
if (address == iaddr.GetBroadcast())
{
NS_LOG_LOGIC("For me (interface broadcast address)");
return true;
}
}
if (address.IsMulticast())
{
#ifdef NOTYET
if (MulticastCheckGroup(iif, address))
#endif
{
NS_LOG_LOGIC("For me (Ipv4Addr multicast address)");
return true;
}
}
if (address.IsBroadcast())
{
NS_LOG_LOGIC("For me (Ipv4Addr broadcast address)");
return true;
}
if (!GetStrongEndSystemModel()) // Check other interfaces
{
for (uint32_t j = 0; j < GetNInterfaces(); j++)
{
if (j == uint32_t(iif))
{
continue;
}
for (uint32_t i = 0; i < GetNAddresses(j); i++)
{
Ipv4InterfaceAddress iaddr = GetAddress(j, i);
if (address == iaddr.GetLocal())
{
NS_LOG_LOGIC("For me (destination " << address
<< " match) on another interface");
return true;
}
// This is a small corner case: match another interface's broadcast address
if (address == iaddr.GetBroadcast())
{
NS_LOG_LOGIC("For me (interface broadcast address on another interface)");
return true;
}
}
}
}
return false;
}
void
Ipv4L3Protocol::Receive(Ptr<NetDevice> device,
Ptr<const Packet> p,
uint16_t protocol,
const Address& from,
const Address& to,
NetDevice::PacketType packetType)
{
NS_LOG_FUNCTION(this << device << p << protocol << from << to << packetType);
NS_LOG_LOGIC("Packet from " << from << " received on node " << m_node->GetId());
int32_t interface = GetInterfaceForDevice(device);
NS_ASSERT_MSG(interface != -1, "Received a packet from an interface that is not known to IPv4");
Ptr<Packet> packet = p->Copy();
Ptr<Ipv4Interface> ipv4Interface = m_interfaces[interface];
if (ipv4Interface->IsUp())
{
m_rxTrace(packet, this, interface);
}
else
{
NS_LOG_LOGIC("Dropping received packet -- interface is down");
Ipv4Header ipHeader;
packet->RemoveHeader(ipHeader);
m_dropTrace(ipHeader, packet, DROP_INTERFACE_DOWN, this, interface);
return;
}
Ipv4Header ipHeader;
if (Node::ChecksumEnabled())
{
ipHeader.EnableChecksum();
}
packet->RemoveHeader(ipHeader);
// Trim any residual frame padding from underlying devices
if (ipHeader.GetPayloadSize() < packet->GetSize())
{
packet->RemoveAtEnd(packet->GetSize() - ipHeader.GetPayloadSize());
}
if (!ipHeader.IsChecksumOk())
{
NS_LOG_LOGIC("Dropping received packet -- checksum not ok");
m_dropTrace(ipHeader, packet, DROP_BAD_CHECKSUM, this, interface);
return;
}
// the packet is valid, we update the ARP cache entry (if present)
Ptr<ArpCache> arpCache = ipv4Interface->GetArpCache();
if (arpCache)
{
// case one, it's a a direct routing.
ArpCache::Entry* entry = arpCache->Lookup(ipHeader.GetSource());
if (entry)
{
if (entry->IsAlive())
{
entry->UpdateSeen();
}
}
else
{
// It's not in the direct routing, so it's the router, and it could have multiple IP
// addresses. In doubt, update all of them. Note: it's a confirmed behavior for Linux
// routers.
std::list<ArpCache::Entry*> entryList = arpCache->LookupInverse(from);
for (auto iter = entryList.begin(); iter != entryList.end(); iter++)
{
if ((*iter)->IsAlive())
{
(*iter)->UpdateSeen();
}
}
}
}
for (auto i = m_sockets.begin(); i != m_sockets.end(); ++i)
{
NS_LOG_LOGIC("Forwarding to raw socket");
Ptr<Ipv4RawSocketImpl> socket = *i;
socket->ForwardUp(packet, ipHeader, ipv4Interface);
}
if (m_enableDpd && ipHeader.GetDestination().IsMulticast() && UpdateDuplicate(packet, ipHeader))
{
NS_LOG_LOGIC("Dropping received packet -- duplicate.");
m_dropTrace(ipHeader, packet, DROP_DUPLICATE, this, interface);
return;
}
NS_ASSERT_MSG(m_routingProtocol, "Need a routing protocol object to process packets");
if (!m_routingProtocol->RouteInput(packet, ipHeader, device, m_ucb, m_mcb, m_lcb, m_ecb))
{
NS_LOG_WARN("No route found for forwarding packet. Drop.");
m_dropTrace(ipHeader, packet, DROP_NO_ROUTE, this, interface);
}
}
Ptr<Icmpv4L4Protocol>
Ipv4L3Protocol::GetIcmp() const
{
Ptr<IpL4Protocol> prot = GetProtocol(Icmpv4L4Protocol::GetStaticProtocolNumber());
if (prot)
{
return prot->GetObject<Icmpv4L4Protocol>();
}
else
{
return nullptr;
}
}
bool
Ipv4L3Protocol::IsUnicast(Ipv4Address ad) const
{
if (ad.IsBroadcast() || ad.IsMulticast())
{
return false;
}
else
{
// check for subnet-broadcast
for (uint32_t ifaceIndex = 0; ifaceIndex < GetNInterfaces(); ifaceIndex++)
{
for (uint32_t j = 0; j < GetNAddresses(ifaceIndex); j++)
{
Ipv4InterfaceAddress ifAddr = GetAddress(ifaceIndex, j);
NS_LOG_LOGIC("Testing address " << ad << " with subnet-directed broadcast "
<< ifAddr.GetBroadcast());
if (ad == ifAddr.GetBroadcast())
{
return false;
}
}
}
}
return true;
}
bool
Ipv4L3Protocol::IsUnicast(Ipv4Address ad, Ipv4Mask interfaceMask) const
{
return !ad.IsMulticast() && !ad.IsSubnetDirectedBroadcast(interfaceMask);
}
void
Ipv4L3Protocol::SendWithHeader(Ptr<Packet> packet, Ipv4Header ipHeader, Ptr<Ipv4Route> route)
{
NS_LOG_FUNCTION(this << packet << ipHeader << route);
if (Node::ChecksumEnabled())
{
ipHeader.EnableChecksum();
}
SendRealOut(route, packet, ipHeader);
}
void
Ipv4L3Protocol::CallTxTrace(const Ipv4Header& ipHeader,
Ptr<Packet> packet,
Ptr<Ipv4> ipv4,
uint32_t interface)
{
if (!m_txTrace.IsEmpty())
{
Ptr<Packet> packetCopy = packet->Copy();
packetCopy->AddHeader(ipHeader);
m_txTrace(packetCopy, ipv4, interface);
}
}
void
Ipv4L3Protocol::Send(Ptr<Packet> packet,
Ipv4Address source,
Ipv4Address destination,
uint8_t protocol,
Ptr<Ipv4Route> route)
{
NS_LOG_FUNCTION(this << packet << source << destination << uint32_t(protocol) << route);
bool mayFragment = true;
// we need a copy of the packet with its tags in case we need to invoke recursion.
Ptr<Packet> pktCopyWithTags = packet->Copy();
uint8_t ttl = m_defaultTtl;
SocketIpTtlTag ipTtlTag;
bool ipTtlTagFound = packet->RemovePacketTag(ipTtlTag);
if (ipTtlTagFound)
{
ttl = ipTtlTag.GetTtl();
}
uint8_t tos = 0;
SocketIpTosTag ipTosTag;
bool ipTosTagFound = packet->RemovePacketTag(ipTosTag);
if (ipTosTagFound)
{
tos = ipTosTag.GetTos();
}
// can construct the header here
Ipv4Header ipHeader =
BuildHeader(source, destination, protocol, packet->GetSize(), ttl, tos, mayFragment);
// Handle a few cases:
// 1) packet is passed in with a route entry
// 1a) packet is passed in with a route entry but route->GetGateway is not set (e.g., on-demand)
// 1b) packet is passed in with a route entry and valid gateway
// 2) packet is passed without a route and packet is destined to limited broadcast address
// 3) packet is passed without a route and packet is destined to a subnet-directed broadcast
// address 4) packet is passed without a route, packet is not broadcast (e.g., a raw socket
// call, or ICMP)
// 1) packet is passed in with route entry
if (route)
{
// 1a) route->GetGateway is not set (e.g., on-demand)
if (!route->GetGateway().IsInitialized())
{
// This could arise because the synchronous RouteOutput() call
// returned to the transport protocol with a source address but
// there was no next hop available yet (since a route may need
// to be queried).
NS_FATAL_ERROR("Ipv4L3Protocol::Send case 1a: packet passed with a route but the "
"Gateway address is uninitialized. This case not yet implemented.");
}
// 1b) with a valid gateway
NS_LOG_LOGIC("Ipv4L3Protocol::Send case 1b: passed in with route and valid gateway");
int32_t interface = GetInterfaceForDevice(route->GetOutputDevice());
m_sendOutgoingTrace(ipHeader, packet, interface);
if (m_enableDpd && ipHeader.GetDestination().IsMulticast())
{
UpdateDuplicate(packet, ipHeader);
}
SendRealOut(route, packet->Copy(), ipHeader);
return;
}
// 2) packet is destined to limited broadcast address or link-local multicast address
if (destination.IsBroadcast() || destination.IsLocalMulticast())
{
NS_LOG_LOGIC("Ipv4L3Protocol::Send case 2: limited broadcast - no route");
uint32_t ifaceIndex = 0;
for (auto ifaceIter = m_interfaces.begin(); ifaceIter != m_interfaces.end();
ifaceIter++, ifaceIndex++)
{
Ptr<Ipv4Interface> outInterface = *ifaceIter;
// ANY source matches any interface
bool sendIt = source.IsAny();
// check if some specific address on outInterface matches
for (uint32_t index = 0; !sendIt && index < outInterface->GetNAddresses(); index++)
{
if (outInterface->GetAddress(index).GetLocal() == source)
{
sendIt = true;
}
}
if (sendIt)
{
// create a proxy route for this interface
Ptr<Ipv4Route> route = Create<Ipv4Route>();
route->SetDestination(destination);
route->SetGateway(Ipv4Address::GetAny());
route->SetSource(source);
route->SetOutputDevice(outInterface->GetDevice());
DecreaseIdentification(source, destination, protocol);
Send(pktCopyWithTags, source, destination, protocol, route);
}
}
return;
}
// 3) check: packet is destined to a subnet-directed broadcast address
for (auto ifaceIter = m_interfaces.begin(); ifaceIter != m_interfaces.end(); ifaceIter++)
{
Ptr<Ipv4Interface> outInterface = *ifaceIter;
uint32_t ifaceIndex = GetInterfaceForDevice(outInterface->GetDevice());
for (uint32_t j = 0; j < GetNAddresses(ifaceIndex); j++)
{
Ipv4InterfaceAddress ifAddr = GetAddress(ifaceIndex, j);
NS_LOG_LOGIC("Testing address " << ifAddr.GetLocal() << " with mask "
<< ifAddr.GetMask());
if (destination.IsSubnetDirectedBroadcast(ifAddr.GetMask()) &&
destination.CombineMask(ifAddr.GetMask()) ==
ifAddr.GetLocal().CombineMask(ifAddr.GetMask()))
{
NS_LOG_LOGIC("Ipv4L3Protocol::Send case 3: subnet directed bcast to "
<< ifAddr.GetLocal() << " - no route");
// create a proxy route for this interface
Ptr<Ipv4Route> route = Create<Ipv4Route>();
route->SetDestination(destination);
route->SetGateway(Ipv4Address::GetAny());
route->SetSource(source);
route->SetOutputDevice(outInterface->GetDevice());
DecreaseIdentification(source, destination, protocol);
Send(pktCopyWithTags, source, destination, protocol, route);
return;
}
}
}
// 4) packet is not broadcast, and route is NULL (e.g., a raw socket call)
NS_LOG_LOGIC("Ipv4L3Protocol::Send case 4: not broadcast and passed in with no route "
<< destination);
Socket::SocketErrno errno_;
Ptr<NetDevice> oif(nullptr); // unused for now
Ptr<Ipv4Route> newRoute;
if (m_routingProtocol)
{
newRoute = m_routingProtocol->RouteOutput(pktCopyWithTags, ipHeader, oif, errno_);
}
else
{
NS_LOG_ERROR("Ipv4L3Protocol::Send: m_routingProtocol == 0");
}
if (newRoute)
{
DecreaseIdentification(source, destination, protocol);
Send(pktCopyWithTags, source, destination, protocol, newRoute);
}
else
{
NS_LOG_WARN("No route to host. Drop.");
m_dropTrace(ipHeader, packet, DROP_NO_ROUTE, this, 0);
DecreaseIdentification(source, destination, protocol);
}
}
void
Ipv4L3Protocol::DecreaseIdentification(Ipv4Address source,
Ipv4Address destination,
uint8_t protocol)
{
uint64_t src = source.Get();
uint64_t dst = destination.Get();
uint64_t srcDst = dst | (src << 32);
std::pair<uint64_t, uint8_t> key = std::make_pair(srcDst, protocol);
m_identification[key]--;
}
Ipv4Header
Ipv4L3Protocol::BuildHeader(Ipv4Address source,
Ipv4Address destination,
uint8_t protocol,
uint16_t payloadSize,
uint8_t ttl,
uint8_t tos,
bool mayFragment)
{
NS_LOG_FUNCTION(this << source << destination << (uint16_t)protocol << payloadSize
<< (uint16_t)ttl << (uint16_t)tos << mayFragment);
Ipv4Header ipHeader;
ipHeader.SetSource(source);
ipHeader.SetDestination(destination);
ipHeader.SetProtocol(protocol);
ipHeader.SetPayloadSize(payloadSize);
ipHeader.SetTtl(ttl);
ipHeader.SetTos(tos);
uint64_t src = source.Get();
uint64_t dst = destination.Get();
uint64_t srcDst = dst | (src << 32);
std::pair<uint64_t, uint8_t> key = std::make_pair(srcDst, protocol);
if (mayFragment)
{
ipHeader.SetMayFragment();
ipHeader.SetIdentification(m_identification[key]);
m_identification[key]++;
}
else
{
ipHeader.SetDontFragment();
// RFC 6864 does not state anything about atomic datagrams
// identification requirement:
// >> Originating sources MAY set the IPv4 ID field of atomic datagrams
// to any value.
ipHeader.SetIdentification(m_identification[key]);
m_identification[key]++;
}
if (Node::ChecksumEnabled())
{
ipHeader.EnableChecksum();
}
return ipHeader;
}
void
Ipv4L3Protocol::SendRealOut(Ptr<Ipv4Route> route, Ptr<Packet> packet, const Ipv4Header& ipHeader)
{
NS_LOG_FUNCTION(this << route << packet << &ipHeader);
if (!route)
{
NS_LOG_WARN("No route to host. Drop.");
m_dropTrace(ipHeader, packet, DROP_NO_ROUTE, this, 0);
return;
}
Ptr<NetDevice> outDev = route->GetOutputDevice();
int32_t interface = GetInterfaceForDevice(outDev);
NS_ASSERT(interface >= 0);
Ptr<Ipv4Interface> outInterface = GetInterface(interface);
NS_LOG_LOGIC("Send via NetDevice ifIndex " << outDev->GetIfIndex() << " ipv4InterfaceIndex "
<< interface);
Ipv4Address target;
std::string targetLabel;
if (route->GetGateway().IsAny())
{
target = ipHeader.GetDestination();
targetLabel = "destination";
}
else
{
target = route->GetGateway();
targetLabel = "gateway";
}
if (outInterface->IsUp())
{
NS_LOG_LOGIC("Send to " << targetLabel << " " << target);
if (packet->GetSize() + ipHeader.GetSerializedSize() > outInterface->GetDevice()->GetMtu())
{
std::list<Ipv4PayloadHeaderPair> listFragments;
DoFragmentation(packet, ipHeader, outInterface->GetDevice()->GetMtu(), listFragments);
for (auto it = listFragments.begin(); it != listFragments.end(); it++)
{
NS_LOG_LOGIC("Sending fragment " << *(it->first));
CallTxTrace(it->second, it->first, this, interface);
outInterface->Send(it->first, it->second, target);
}
}
else
{
CallTxTrace(ipHeader, packet, this, interface);
outInterface->Send(packet, ipHeader, target);
}
}
}
// This function analogous to Linux ip_mr_forward()
void
Ipv4L3Protocol::IpMulticastForward(Ptr<Ipv4MulticastRoute> mrtentry,
Ptr<const Packet> p,
const Ipv4Header& header)
{
NS_LOG_FUNCTION(this << mrtentry << p << header);
NS_LOG_LOGIC("Multicast forwarding logic for node: " << m_node->GetId());
std::map<uint32_t, uint32_t> ttlMap = mrtentry->GetOutputTtlMap();
for (auto mapIter = ttlMap.begin(); mapIter != ttlMap.end(); mapIter++)
{
uint32_t interface = mapIter->first;
// uint32_t outputTtl = mapIter->second; // Unused for now
Ptr<Packet> packet = p->Copy();
Ipv4Header ipHeader = header;
if (ipHeader.GetTtl() <= 1)
{
NS_LOG_WARN("TTL exceeded. Drop.");
m_dropTrace(header, packet, DROP_TTL_EXPIRED, this, interface);
return;
}
ipHeader.SetTtl(header.GetTtl() - 1);
NS_LOG_LOGIC("Forward multicast via interface " << interface);
Ptr<Ipv4Route> rtentry = Create<Ipv4Route>();
rtentry->SetSource(ipHeader.GetSource());
rtentry->SetDestination(ipHeader.GetDestination());
rtentry->SetGateway(Ipv4Address::GetAny());
rtentry->SetOutputDevice(GetNetDevice(interface));
m_multicastForwardTrace(ipHeader, packet, interface);
SendRealOut(rtentry, packet, ipHeader);
}
}
// This function analogous to Linux ip_forward()
void
Ipv4L3Protocol::IpForward(Ptr<Ipv4Route> rtentry, Ptr<const Packet> p, const Ipv4Header& header)
{
NS_LOG_FUNCTION(this << rtentry << p << header);
NS_LOG_LOGIC("Forwarding logic for node: " << m_node->GetId());
// Forwarding
Ipv4Header ipHeader = header;
Ptr<Packet> packet = p->Copy();
int32_t interface = GetInterfaceForDevice(rtentry->GetOutputDevice());
if (ipHeader.GetTtl() <= 1)
{
// Do not reply to multicast/broadcast IP address
if (!ipHeader.GetDestination().IsBroadcast() && !ipHeader.GetDestination().IsMulticast())
{
Ptr<Icmpv4L4Protocol> icmp = GetIcmp();
icmp->SendTimeExceededTtl(ipHeader, packet, false);
}
NS_LOG_WARN("TTL exceeded. Drop.");
m_dropTrace(header, packet, DROP_TTL_EXPIRED, this, interface);
return;
}
ipHeader.SetTtl(ipHeader.GetTtl() - 1);
// in case the packet still has a priority tag attached, remove it
SocketPriorityTag priorityTag;
packet->RemovePacketTag(priorityTag);
uint8_t priority = Socket::IpTos2Priority(ipHeader.GetTos());
// add a priority tag if the priority is not null
if (priority)
{
priorityTag.SetPriority(priority);
packet->AddPacketTag(priorityTag);
}
m_unicastForwardTrace(ipHeader, packet, interface);
SendRealOut(rtentry, packet, ipHeader);
}
void
Ipv4L3Protocol::LocalDeliver(Ptr<const Packet> packet, const Ipv4Header& ip, uint32_t iif)
{
NS_LOG_FUNCTION(this << packet << &ip << iif);
Ptr<Packet> p = packet->Copy(); // need to pass a non-const packet up
Ipv4Header ipHeader = ip;
if (!ipHeader.IsLastFragment() || ipHeader.GetFragmentOffset() != 0)
{
NS_LOG_LOGIC("Received a fragment, processing " << *p);
bool isPacketComplete;
isPacketComplete = ProcessFragment(p, ipHeader, iif);
if (!isPacketComplete)
{
return;
}
NS_LOG_LOGIC("Got last fragment, Packet is complete " << *p);
ipHeader.SetFragmentOffset(0);
ipHeader.SetPayloadSize(p->GetSize());
}
m_localDeliverTrace(ipHeader, p, iif);
Ptr<IpL4Protocol> protocol = GetProtocol(ipHeader.GetProtocol(), iif);
if (protocol)
{
// we need to make a copy in the unlikely event we hit the
// RX_ENDPOINT_UNREACH codepath
Ptr<Packet> copy = p->Copy();
IpL4Protocol::RxStatus status = protocol->Receive(p, ipHeader, GetInterface(iif));
switch (status)
{
case IpL4Protocol::RX_OK:
// fall through
case IpL4Protocol::RX_ENDPOINT_CLOSED:
// fall through
case IpL4Protocol::RX_CSUM_FAILED:
break;
case IpL4Protocol::RX_ENDPOINT_UNREACH:
if (ipHeader.GetDestination().IsBroadcast() || ipHeader.GetDestination().IsMulticast())
{
break; // Do not reply to broadcast or multicast
}
// Another case to suppress ICMP is a subnet-directed broadcast
bool subnetDirected = false;
for (uint32_t i = 0; i < GetNAddresses(iif); i++)
{
Ipv4InterfaceAddress addr = GetAddress(iif, i);
if (addr.GetLocal().CombineMask(addr.GetMask()) ==
ipHeader.GetDestination().CombineMask(addr.GetMask()) &&
ipHeader.GetDestination().IsSubnetDirectedBroadcast(addr.GetMask()))
{
subnetDirected = true;
}
}
if (!subnetDirected)
{
GetIcmp()->SendDestUnreachPort(ipHeader, copy);
}
}
}
}
bool
Ipv4L3Protocol::AddAddress(uint32_t i, Ipv4InterfaceAddress address)
{
NS_LOG_FUNCTION(this << i << address);
Ptr<Ipv4Interface> interface = GetInterface(i);
bool retVal = interface->AddAddress(address);
if (m_routingProtocol)
{
m_routingProtocol->NotifyAddAddress(i, address);
}
return retVal;
}
Ipv4InterfaceAddress
Ipv4L3Protocol::GetAddress(uint32_t interfaceIndex, uint32_t addressIndex) const
{
Ptr<Ipv4Interface> interface = GetInterface(interfaceIndex);
return interface->GetAddress(addressIndex);
}
uint32_t
Ipv4L3Protocol::GetNAddresses(uint32_t interface) const
{
Ptr<Ipv4Interface> iface = GetInterface(interface);
return iface->GetNAddresses();
}
bool
Ipv4L3Protocol::RemoveAddress(uint32_t i, uint32_t addressIndex)
{
NS_LOG_FUNCTION(this << i << addressIndex);
Ptr<Ipv4Interface> interface = GetInterface(i);
Ipv4InterfaceAddress address = interface->RemoveAddress(addressIndex);
if (address != Ipv4InterfaceAddress())
{
if (m_routingProtocol)
{
m_routingProtocol->NotifyRemoveAddress(i, address);
}
return true;
}
return false;
}
bool
Ipv4L3Protocol::RemoveAddress(uint32_t i, Ipv4Address address)
{
NS_LOG_FUNCTION(this << i << address);
if (address == Ipv4Address::GetLoopback())
{
NS_LOG_WARN("Cannot remove loopback address.");
return false;
}
Ptr<Ipv4Interface> interface = GetInterface(i);
Ipv4InterfaceAddress ifAddr = interface->RemoveAddress(address);
if (ifAddr != Ipv4InterfaceAddress())
{
if (m_routingProtocol)
{
m_routingProtocol->NotifyRemoveAddress(i, ifAddr);
}
return true;
}
return false;
}
Ipv4Address
Ipv4L3Protocol::SourceAddressSelection(uint32_t interfaceIdx, Ipv4Address dest)
{
NS_LOG_FUNCTION(this << interfaceIdx << " " << dest);
if (GetNAddresses(interfaceIdx) == 1) // common case
{
return GetAddress(interfaceIdx, 0).GetLocal();
}
// no way to determine the scope of the destination, so adopt the
// following rule: pick the first available address (index 0) unless
// a subsequent address is on link (in which case, pick the primary
// address if there are multiple)
Ipv4Address candidate = GetAddress(interfaceIdx, 0).GetLocal();
for (uint32_t i = 0; i < GetNAddresses(interfaceIdx); i++)
{
Ipv4InterfaceAddress test = GetAddress(interfaceIdx, i);
if (test.GetLocal().CombineMask(test.GetMask()) == dest.CombineMask(test.GetMask()))
{
if (!test.IsSecondary())
{
return test.GetLocal();
}
}
}
return candidate;
}
Ipv4Address
Ipv4L3Protocol::SelectSourceAddress(Ptr<const NetDevice> device,
Ipv4Address dst,
Ipv4InterfaceAddress::InterfaceAddressScope_e scope)
{
NS_LOG_FUNCTION(this << device << dst << scope);
Ipv4Address addr("0.0.0.0");
Ipv4InterfaceAddress iaddr;
bool found = false;
if (device)
{
int32_t i = GetInterfaceForDevice(device);
NS_ASSERT_MSG(i >= 0, "No device found on node");
for (uint32_t j = 0; j < GetNAddresses(i); j++)
{
iaddr = GetAddress(i, j);
if (iaddr.IsSecondary())
{
continue;
}
if (iaddr.GetScope() > scope)
{
continue;
}
if (dst.CombineMask(iaddr.GetMask()) == iaddr.GetLocal().CombineMask(iaddr.GetMask()))
{
return iaddr.GetLocal();
}
if (!found)
{
addr = iaddr.GetLocal();
found = true;
}
}
}
if (found)
{
return addr;
}
// Iterate among all interfaces
for (uint32_t i = 0; i < GetNInterfaces(); i++)
{
for (uint32_t j = 0; j < GetNAddresses(i); j++)
{
iaddr = GetAddress(i, j);
if (iaddr.IsSecondary())
{
continue;
}
if (iaddr.GetScope() != Ipv4InterfaceAddress::LINK && iaddr.GetScope() <= scope)
{
return iaddr.GetLocal();
}
}
}
NS_LOG_WARN("Could not find source address for " << dst << " and scope " << scope
<< ", returning 0");
return addr;
}
void
Ipv4L3Protocol::SetMetric(uint32_t i, uint16_t metric)
{
NS_LOG_FUNCTION(this << i << metric);
Ptr<Ipv4Interface> interface = GetInterface(i);
interface->SetMetric(metric);
}
uint16_t
Ipv4L3Protocol::GetMetric(uint32_t i) const
{
Ptr<Ipv4Interface> interface = GetInterface(i);
return interface->GetMetric();
}
uint16_t
Ipv4L3Protocol::GetMtu(uint32_t i) const
{
Ptr<Ipv4Interface> interface = GetInterface(i);
return interface->GetDevice()->GetMtu();
}
bool
Ipv4L3Protocol::IsUp(uint32_t i) const
{
Ptr<Ipv4Interface> interface = GetInterface(i);
return interface->IsUp();
}
void
Ipv4L3Protocol::SetUp(uint32_t i)
{
NS_LOG_FUNCTION(this << i);
Ptr<Ipv4Interface> interface = GetInterface(i);
// RFC 791, pg.25:
// Every internet module must be able to forward a datagram of 68
// octets without further fragmentation. This is because an internet
// header may be up to 60 octets, and the minimum fragment is 8 octets.
if (interface->GetDevice()->GetMtu() >= 68)
{
interface->SetUp();
if (m_routingProtocol)
{
m_routingProtocol->NotifyInterfaceUp(i);
}
}
else
{
NS_LOG_LOGIC(
"Interface "
<< int(i)
<< " is set to be down for IPv4. Reason: not respecting minimum IPv4 MTU (68 octets)");
}
}
void
Ipv4L3Protocol::SetDown(uint32_t ifaceIndex)
{
NS_LOG_FUNCTION(this << ifaceIndex);
Ptr<Ipv4Interface> interface = GetInterface(ifaceIndex);
interface->SetDown();
if (m_routingProtocol)
{
m_routingProtocol->NotifyInterfaceDown(ifaceIndex);
}
}
bool
Ipv4L3Protocol::IsForwarding(uint32_t i) const
{
Ptr<Ipv4Interface> interface = GetInterface(i);
NS_LOG_LOGIC("Forwarding state: " << interface->IsForwarding());
return interface->IsForwarding();
}
void
Ipv4L3Protocol::SetForwarding(uint32_t i, bool val)
{
NS_LOG_FUNCTION(this << i);
Ptr<Ipv4Interface> interface = GetInterface(i);
interface->SetForwarding(val);
}
Ptr<NetDevice>
Ipv4L3Protocol::GetNetDevice(uint32_t i)
{
return GetInterface(i)->GetDevice();
}
void
Ipv4L3Protocol::SetIpForward(bool forward)
{
NS_LOG_FUNCTION(this << forward);
m_ipForward = forward;
for (auto i = m_interfaces.begin(); i != m_interfaces.end(); i++)
{
(*i)->SetForwarding(forward);
}
}
bool
Ipv4L3Protocol::GetIpForward() const
{
return m_ipForward;
}
void
Ipv4L3Protocol::SetWeakEsModel(bool model)
{
NS_LOG_FUNCTION(this << model);
m_strongEndSystemModel = !model;
}
bool
Ipv4L3Protocol::GetWeakEsModel() const
{
return !m_strongEndSystemModel;
}
void
Ipv4L3Protocol::SetStrongEndSystemModel(bool model)
{
NS_LOG_FUNCTION(this << model);
m_strongEndSystemModel = model;
}
bool
Ipv4L3Protocol::GetStrongEndSystemModel() const
{
return m_strongEndSystemModel;
}
void
Ipv4L3Protocol::RouteInputError(Ptr<const Packet> p,
const Ipv4Header& ipHeader,
Socket::SocketErrno sockErrno)
{
NS_LOG_FUNCTION(this << p << ipHeader << sockErrno);
NS_LOG_LOGIC("Route input failure-- dropping packet to " << ipHeader << " with errno "
<< sockErrno);
m_dropTrace(ipHeader, p, DROP_ROUTE_ERROR, this, 0);
// \todo Send an ICMP no route.
}
void
Ipv4L3Protocol::DoFragmentation(Ptr<Packet> packet,
const Ipv4Header& ipv4Header,
uint32_t outIfaceMtu,
std::list<Ipv4PayloadHeaderPair>& listFragments)
{
// BEWARE: here we do assume that the header options are not present.
// a much more complex handling is necessary in case there are options.
// If (when) IPv4 option headers will be implemented, the following code shall be changed.
// Of course also the reassembly code shall be changed as well.
NS_LOG_FUNCTION(this << *packet << outIfaceMtu << &listFragments);
Ptr<Packet> p = packet->Copy();
NS_ASSERT_MSG((ipv4Header.GetSerializedSize() == 5 * 4),
"IPv4 fragmentation implementation only works without option headers.");
uint16_t offset = 0;
bool moreFragment = true;
uint16_t originalOffset = ipv4Header.GetFragmentOffset();
bool isLastFragment = ipv4Header.IsLastFragment();
uint32_t currentFragmentablePartSize = 0;
// IPv4 fragments are all 8 bytes aligned but the last.
// The IP payload size is:
// floor( ( outIfaceMtu - ipv4Header.GetSerializedSize() ) /8 ) *8
uint32_t fragmentSize = (outIfaceMtu - ipv4Header.GetSerializedSize()) & ~uint32_t(0x7);
NS_LOG_LOGIC("Fragmenting - Target Size: " << fragmentSize);
do
{
Ipv4Header fragmentHeader = ipv4Header;
if (p->GetSize() > offset + fragmentSize)
{
moreFragment = true;
currentFragmentablePartSize = fragmentSize;
fragmentHeader.SetMoreFragments();
}
else
{
moreFragment = false;
currentFragmentablePartSize = p->GetSize() - offset;
if (!isLastFragment)
{
fragmentHeader.SetMoreFragments();
}
else
{
fragmentHeader.SetLastFragment();
}
}
NS_LOG_LOGIC("Fragment creation - " << offset << ", " << currentFragmentablePartSize);
Ptr<Packet> fragment = p->CreateFragment(offset, currentFragmentablePartSize);
NS_LOG_LOGIC("Fragment created - " << offset << ", " << fragment->GetSize());
fragmentHeader.SetFragmentOffset(offset + originalOffset);
fragmentHeader.SetPayloadSize(currentFragmentablePartSize);
if (Node::ChecksumEnabled())
{
fragmentHeader.EnableChecksum();
}
NS_LOG_LOGIC("Fragment check - " << fragmentHeader.GetFragmentOffset());
NS_LOG_LOGIC("New fragment Header " << fragmentHeader);
std::ostringstream oss;
oss << fragmentHeader;
fragment->Print(oss);
NS_LOG_LOGIC("New fragment " << *fragment);
listFragments.emplace_back(fragment, fragmentHeader);
offset += currentFragmentablePartSize;
} while (moreFragment);
}
bool
Ipv4L3Protocol::ProcessFragment(Ptr<Packet>& packet, Ipv4Header& ipHeader, uint32_t iif)
{
NS_LOG_FUNCTION(this << packet << ipHeader << iif);
uint64_t addressCombination =
uint64_t(ipHeader.GetSource().Get()) << 32 | uint64_t(ipHeader.GetDestination().Get());
uint32_t idProto =
uint32_t(ipHeader.GetIdentification()) << 16 | uint32_t(ipHeader.GetProtocol());
FragmentKey_t key;
bool ret = false;
Ptr<Packet> p = packet->Copy();
key.first = addressCombination;
key.second = idProto;
Ptr<Fragments> fragments;
auto it = m_fragments.find(key);
if (it == m_fragments.end())
{
fragments = Create<Fragments>();
m_fragments.insert(std::make_pair(key, fragments));
auto iter = SetTimeout(key, ipHeader, iif);
fragments->SetTimeoutIter(iter);
}
else
{
fragments = it->second;
}
NS_LOG_LOGIC("Adding fragment - Size: " << packet->GetSize()
<< " - Offset: " << (ipHeader.GetFragmentOffset()));
fragments->AddFragment(p, ipHeader.GetFragmentOffset(), !ipHeader.IsLastFragment());
if (fragments->IsEntire())
{
packet = fragments->GetPacket();
m_timeoutEventList.erase(fragments->GetTimeoutIter());
fragments = nullptr;
m_fragments.erase(key);
ret = true;
}
return ret;
}
Ipv4L3Protocol::Fragments::Fragments()
: m_moreFragment(false)
{
NS_LOG_FUNCTION(this);
}
void
Ipv4L3Protocol::Fragments::AddFragment(Ptr<Packet> fragment,
uint16_t fragmentOffset,
bool moreFragment)
{
NS_LOG_FUNCTION(this << fragment << fragmentOffset << moreFragment);
std::list<std::pair<Ptr<Packet>, uint16_t>>::iterator it;
for (it = m_fragments.begin(); it != m_fragments.end(); it++)
{
if (it->second > fragmentOffset)
{
break;
}
}
if (it == m_fragments.end())
{
m_moreFragment = moreFragment;
}
m_fragments.insert(it, std::pair<Ptr<Packet>, uint16_t>(fragment, fragmentOffset));
}
bool
Ipv4L3Protocol::Fragments::IsEntire() const
{
NS_LOG_FUNCTION(this);
bool ret = !m_moreFragment && !m_fragments.empty();
if (ret)
{
uint16_t lastEndOffset = 0;
for (auto it = m_fragments.begin(); it != m_fragments.end(); it++)
{
// overlapping fragments do exist
NS_LOG_LOGIC("Checking overlaps " << lastEndOffset << " - " << it->second);
if (lastEndOffset < it->second)
{
ret = false;
break;
}
// fragments might overlap in strange ways
uint16_t fragmentEnd = it->first->GetSize() + it->second;
lastEndOffset = std::max(lastEndOffset, fragmentEnd);
}
}
return ret;
}
Ptr<Packet>
Ipv4L3Protocol::Fragments::GetPacket() const
{
NS_LOG_FUNCTION(this);
auto it = m_fragments.begin();
Ptr<Packet> p = it->first->Copy();
uint16_t lastEndOffset = p->GetSize();
it++;
for (; it != m_fragments.end(); it++)
{
if (lastEndOffset > it->second)
{
// The fragments are overlapping.
// We do not overwrite the "old" with the "new" because we do not know when each
// arrived. This is different from what Linux does. It is not possible to emulate a
// fragmentation attack.
uint32_t newStart = lastEndOffset - it->second;
if (it->first->GetSize() > newStart)
{
uint32_t newSize = it->first->GetSize() - newStart;
Ptr<Packet> tempFragment = it->first->CreateFragment(newStart, newSize);
p->AddAtEnd(tempFragment);
}
}
else
{
NS_LOG_LOGIC("Adding: " << *(it->first));
p->AddAtEnd(it->first);
}
lastEndOffset = p->GetSize();
}
return p;
}
Ptr<Packet>
Ipv4L3Protocol::Fragments::GetPartialPacket() const
{
NS_LOG_FUNCTION(this);
auto it = m_fragments.begin();
Ptr<Packet> p = Create<Packet>();
uint16_t lastEndOffset = 0;
if (m_fragments.begin()->second > 0)
{
return p;
}
for (it = m_fragments.begin(); it != m_fragments.end(); it++)
{
if (lastEndOffset > it->second)
{
uint32_t newStart = lastEndOffset - it->second;
uint32_t newSize = it->first->GetSize() - newStart;
Ptr<Packet> tempFragment = it->first->CreateFragment(newStart, newSize);
p->AddAtEnd(tempFragment);
}
else if (lastEndOffset == it->second)
{
NS_LOG_LOGIC("Adding: " << *(it->first));
p->AddAtEnd(it->first);
}
lastEndOffset = p->GetSize();
}
return p;
}
void
Ipv4L3Protocol::Fragments::SetTimeoutIter(FragmentsTimeoutsListI_t iter)
{
m_timeoutIter = iter;
}
Ipv4L3Protocol::FragmentsTimeoutsListI_t
Ipv4L3Protocol::Fragments::GetTimeoutIter()
{
return m_timeoutIter;
}
void
Ipv4L3Protocol::HandleFragmentsTimeout(FragmentKey_t key, Ipv4Header& ipHeader, uint32_t iif)
{
NS_LOG_FUNCTION(this << &key << &ipHeader << iif);
auto it = m_fragments.find(key);
Ptr<Packet> packet = it->second->GetPartialPacket();
// if we have at least 8 bytes, we can send an ICMP.
if (packet->GetSize() > 8)
{
Ptr<Icmpv4L4Protocol> icmp = GetIcmp();
icmp->SendTimeExceededTtl(ipHeader, packet, true);
}
m_dropTrace(ipHeader, packet, DROP_FRAGMENT_TIMEOUT, this, iif);
// clear the buffers
it->second = nullptr;
m_fragments.erase(key);
}
bool
Ipv4L3Protocol::UpdateDuplicate(Ptr<const Packet> p, const Ipv4Header& header)
{
NS_LOG_FUNCTION(this << p << header);
// \todo RFC 6621 mandates SHA-1 hash. For now ns3 hash should be fine.
uint8_t proto = header.GetProtocol();
Ipv4Address src = header.GetSource();
Ipv4Address dst = header.GetDestination();
uint64_t id = header.GetIdentification();
// concat hash value onto id
uint64_t hash = id << 32;
if (header.GetFragmentOffset() || !header.IsLastFragment())
{
// use I-DPD (RFC 6621, Sec 6.2.1)
hash |= header.GetFragmentOffset();
}
else
{
// use H-DPD (RFC 6621, Sec 6.2.2)
// serialize packet
Ptr<Packet> pkt = p->Copy();
pkt->AddHeader(header);
std::ostringstream oss(std::ios_base::binary);
pkt->CopyData(&oss, pkt->GetSize());
std::string bytes = oss.str();
NS_ASSERT_MSG(bytes.size() >= 20, "Degenerate header serialization");
// zero out mutable fields
bytes[1] = 0; // DSCP / ECN
bytes[6] = bytes[7] = 0; // Flags / Fragment offset
bytes[8] = 0; // TTL
bytes[10] = bytes[11] = 0; // Header checksum
if (header.GetSerializedSize() > 20) // assume options should be 0'd
{
std::fill_n(bytes.begin() + 20, header.GetSerializedSize() - 20, 0);
}
// concat hash onto ID
hash |= (uint64_t)Hash32(bytes);
}
// set cleanup job for new duplicate entries
if (!m_cleanDpd.IsPending() && m_purge.IsStrictlyPositive())
{
m_cleanDpd = Simulator::Schedule(m_expire, &Ipv4L3Protocol::RemoveDuplicates, this);
}
// assume this is a new entry
DupTuple_t key{hash, proto, src, dst};
NS_LOG_DEBUG("Packet " << p->GetUid() << " key = (" << std::hex << std::get<0>(key) << ", "
<< std::dec << +std::get<1>(key) << ", " << std::get<2>(key) << ", "
<< std::get<3>(key) << ")");
// place a new entry, on collision the existing entry iterator is returned
auto [iter, inserted] = m_dups.emplace(key, Seconds(0));
bool isDup = !inserted && iter->second > Simulator::Now();
// set the expiration event
iter->second = Simulator::Now() + m_expire;
return isDup;
}
void
Ipv4L3Protocol::RemoveDuplicates()
{
NS_LOG_FUNCTION(this);
DupMap_t::size_type n = 0;
Time expire = Simulator::Now();
auto iter = m_dups.cbegin();
while (iter != m_dups.cend())
{
if (iter->second < expire)
{
NS_LOG_LOGIC("Remove key = (" << std::hex << std::get<0>(iter->first) << ", "
<< std::dec << +std::get<1>(iter->first) << ", "
<< std::get<2>(iter->first) << ", "
<< std::get<3>(iter->first) << ")");
iter = m_dups.erase(iter);
++n;
}
else
{
++iter;
}
}
NS_LOG_DEBUG("Purged " << n << " expired duplicate entries out of " << (n + m_dups.size()));
// keep cleaning up if necessary
if (!m_dups.empty() && m_purge.IsStrictlyPositive())
{
m_cleanDpd = Simulator::Schedule(m_purge, &Ipv4L3Protocol::RemoveDuplicates, this);
}
}
Ipv4L3Protocol::FragmentsTimeoutsListI_t
Ipv4L3Protocol::SetTimeout(FragmentKey_t key, Ipv4Header ipHeader, uint32_t iif)
{
Time now = Simulator::Now() + m_fragmentExpirationTimeout;
if (m_timeoutEventList.empty())
{
m_timeoutEvent =
Simulator::Schedule(m_fragmentExpirationTimeout, &Ipv4L3Protocol::HandleTimeout, this);
}
m_timeoutEventList.emplace_back(now, key, ipHeader, iif);
auto iter = --m_timeoutEventList.end();
return iter;
}
void
Ipv4L3Protocol::HandleTimeout()
{
Time now = Simulator::Now();
while (!m_timeoutEventList.empty() && std::get<0>(*m_timeoutEventList.begin()) == now)
{
HandleFragmentsTimeout(std::get<1>(*m_timeoutEventList.begin()),
std::get<2>(*m_timeoutEventList.begin()),
std::get<3>(*m_timeoutEventList.begin()));
m_timeoutEventList.pop_front();
}
if (m_timeoutEventList.empty())
{
return;
}
Time difference = std::get<0>(*m_timeoutEventList.begin()) - now;
m_timeoutEvent = Simulator::Schedule(difference, &Ipv4L3Protocol::HandleTimeout, this);
}
} // namespace ns3