internet: (Merge !102) Add RFC6621 IPv4 layer de-duplication
This commit is contained in:
committed by
Tommaso Pecorella
parent
ad3324e2dd
commit
60dc6f91b9
@@ -27,6 +27,8 @@
|
|||||||
#include "ns3/socket.h"
|
#include "ns3/socket.h"
|
||||||
#include "ns3/net-device.h"
|
#include "ns3/net-device.h"
|
||||||
#include "ns3/uinteger.h"
|
#include "ns3/uinteger.h"
|
||||||
|
#include "ns3/string.h"
|
||||||
|
#include "ns3/boolean.h"
|
||||||
#include "ns3/trace-source-accessor.h"
|
#include "ns3/trace-source-accessor.h"
|
||||||
#include "ns3/object-vector.h"
|
#include "ns3/object-vector.h"
|
||||||
#include "ns3/ipv4-header.h"
|
#include "ns3/ipv4-header.h"
|
||||||
@@ -69,6 +71,15 @@ Ipv4L3Protocol::GetTypeId (void)
|
|||||||
TimeValue (Seconds (30)),
|
TimeValue (Seconds (30)),
|
||||||
MakeTimeAccessor (&Ipv4L3Protocol::m_fragmentExpirationTimeout),
|
MakeTimeAccessor (&Ipv4L3Protocol::m_fragmentExpirationTimeout),
|
||||||
MakeTimeChecker ())
|
MakeTimeChecker ())
|
||||||
|
.AddAttribute ("EnableRFC6621",
|
||||||
|
"Enable RFC 6621 packet de-duplication",
|
||||||
|
BooleanValue (false),
|
||||||
|
MakeBooleanAccessor (&Ipv4L3Protocol::m_enableRfc6621),
|
||||||
|
MakeBooleanChecker ())
|
||||||
|
.AddAttribute ("DuplicateExpire", "Expiration delay for duplicate cache entries",
|
||||||
|
TimeValue (MilliSeconds (1)),
|
||||||
|
MakeTimeAccessor (&Ipv4L3Protocol::m_expire),
|
||||||
|
MakeTimeChecker ())
|
||||||
.AddTraceSource ("Tx",
|
.AddTraceSource ("Tx",
|
||||||
"Send ipv4 packet to outgoing interface.",
|
"Send ipv4 packet to outgoing interface.",
|
||||||
MakeTraceSourceAccessor (&Ipv4L3Protocol::m_txTrace),
|
MakeTraceSourceAccessor (&Ipv4L3Protocol::m_txTrace),
|
||||||
@@ -318,6 +329,15 @@ Ipv4L3Protocol::DoDispose (void)
|
|||||||
m_fragments.clear ();
|
m_fragments.clear ();
|
||||||
m_fragmentsTimers.clear ();
|
m_fragmentsTimers.clear ();
|
||||||
|
|
||||||
|
for (auto dup: m_dups)
|
||||||
|
{
|
||||||
|
if (dup.second.IsRunning ())
|
||||||
|
{
|
||||||
|
dup.second.Cancel ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_dups.clear ();
|
||||||
|
|
||||||
Object::DoDispose ();
|
Object::DoDispose ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,7 +526,7 @@ Ipv4L3Protocol::IsDestinationAddress (Ipv4Address address, uint32_t iif) const
|
|||||||
#endif
|
#endif
|
||||||
if (true)
|
if (true)
|
||||||
{
|
{
|
||||||
NS_LOG_LOGIC ("For me (Ipv4Addr multicast address");
|
NS_LOG_LOGIC ("For me (Ipv4Addr multicast address)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -629,6 +649,13 @@ Ipv4L3Protocol::Receive ( Ptr<NetDevice> device, Ptr<const Packet> p, uint16_t p
|
|||||||
socket->ForwardUp (packet, ipHeader, ipv4Interface);
|
socket->ForwardUp (packet, ipHeader, ipv4Interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_enableRfc6621 && ipHeader.GetDestination ().IsMulticast () && UpdateDuplicate (packet, ipHeader))
|
||||||
|
{
|
||||||
|
NS_LOG_LOGIC ("Dropping received packet -- duplicate.");
|
||||||
|
m_dropTrace (ipHeader, packet, DROP_DUPLICATE, m_node->GetObject<Ipv4> (), interface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NS_ASSERT_MSG (m_routingProtocol != 0, "Need a routing protocol object to process packets");
|
NS_ASSERT_MSG (m_routingProtocol != 0, "Need a routing protocol object to process packets");
|
||||||
if (!m_routingProtocol->RouteInput (packet, ipHeader, device,
|
if (!m_routingProtocol->RouteInput (packet, ipHeader, device,
|
||||||
MakeCallback (&Ipv4L3Protocol::IpForward, this),
|
MakeCallback (&Ipv4L3Protocol::IpForward, this),
|
||||||
@@ -1697,4 +1724,85 @@ Ipv4L3Protocol::HandleFragmentsTimeout (std::pair<uint64_t, uint32_t> key, Ipv4H
|
|||||||
m_fragments.erase (key);
|
m_fragments.erase (key);
|
||||||
m_fragmentsTimers.erase (key);
|
m_fragmentsTimers.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assume this is a new entry
|
||||||
|
bool isDup = false;
|
||||||
|
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 = m_dups.emplace_hint (m_dups.end (), key, EventId ());
|
||||||
|
// cancel un-expired event
|
||||||
|
if (iter->second.IsRunning ())
|
||||||
|
{
|
||||||
|
iter->second.Cancel ();
|
||||||
|
isDup = true; // flag this was a duplicate
|
||||||
|
}
|
||||||
|
// set the expiration event
|
||||||
|
iter->second = Simulator::Schedule (m_expire, &Ipv4L3Protocol::RemoveDuplicate, this, iter);
|
||||||
|
return isDup;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Ipv4L3Protocol::RemoveDuplicate (DupMap_t::const_iterator iter)
|
||||||
|
{
|
||||||
|
NS_LOG_FUNCTION (this);
|
||||||
|
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) << ")");
|
||||||
|
|
||||||
|
m_dups.erase (iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace ns3
|
} // namespace ns3
|
||||||
|
|||||||
@@ -101,7 +101,8 @@ public:
|
|||||||
DROP_BAD_CHECKSUM, /**< Bad checksum */
|
DROP_BAD_CHECKSUM, /**< Bad checksum */
|
||||||
DROP_INTERFACE_DOWN, /**< Interface is down so can not send packet */
|
DROP_INTERFACE_DOWN, /**< Interface is down so can not send packet */
|
||||||
DROP_ROUTE_ERROR, /**< Route error */
|
DROP_ROUTE_ERROR, /**< Route error */
|
||||||
DROP_FRAGMENT_TIMEOUT /**< Fragment timeout exceeded */
|
DROP_FRAGMENT_TIMEOUT, /**< Fragment timeout exceeded */
|
||||||
|
DROP_DUPLICATE /**< Duplicate packet received */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -556,6 +557,29 @@ private:
|
|||||||
Time m_fragmentExpirationTimeout; //!< Expiration timeout
|
Time m_fragmentExpirationTimeout; //!< Expiration timeout
|
||||||
MapFragmentsTimers_t m_fragmentsTimers; //!< Expiration events.
|
MapFragmentsTimers_t m_fragmentsTimers; //!< Expiration events.
|
||||||
|
|
||||||
|
/// IETF RFC 6621, Section 6.2 de-duplication w/o IPSec
|
||||||
|
/// RFC 6621 recommended duplicate packet tuple: {IPV hash, IP protocol, IP source address, IP destination address}
|
||||||
|
typedef std::tuple <uint64_t, uint8_t, Ipv4Address, Ipv4Address> DupTuple_t;
|
||||||
|
/// Maps packet duplicate tuple to expiration event
|
||||||
|
typedef std::map<DupTuple_t, EventId> DupMap_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers duplicate entry, return false if new
|
||||||
|
* \param [in] p Possibly duplicate packet.
|
||||||
|
* \param [in] header Packet \p p header.
|
||||||
|
* \return True if this packet is a duplicate
|
||||||
|
*/
|
||||||
|
bool UpdateDuplicate (Ptr<const Packet> p, const Ipv4Header &header);
|
||||||
|
/**
|
||||||
|
* Remove duplicate packet entry
|
||||||
|
* \param [in] iter Iterator into duplicate map to remove
|
||||||
|
*/
|
||||||
|
void RemoveDuplicate (DupMap_t::const_iterator iter);
|
||||||
|
|
||||||
|
bool m_enableRfc6621; //!< Enable RFC 6621 de-duplication
|
||||||
|
DupMap_t m_dups; //!< map of packet duplicate tuples to expiry event
|
||||||
|
Time m_expire; //!< duplicate entry expiration delay
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Namespace ns3
|
} // Namespace ns3
|
||||||
|
|||||||
500
src/internet/test/ipv4-deduplication-test.cc
Normal file
500
src/internet/test/ipv4-deduplication-test.cc
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Universita' di Firenze
|
||||||
|
* Copyright (c) 2019 WPL, Inc. : RFC 6621 mutlicast packet de-duplication
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
* Modified (2019): Jared Dulmage <jared.dulmage@wpli.net>
|
||||||
|
* Tests dissemination of multicast packets across a mesh
|
||||||
|
* network to all nodes over multiple hops. Tests check
|
||||||
|
* the number of received packets and dropped packets
|
||||||
|
* with RFC 6621 de-duplication enabled or disabled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ns3/test.h"
|
||||||
|
#include "ns3/simulator.h"
|
||||||
|
#include "ns3/simple-channel.h"
|
||||||
|
#include "ns3/simple-net-device.h"
|
||||||
|
#include "ns3/socket.h"
|
||||||
|
#include "ns3/boolean.h"
|
||||||
|
#include "ns3/double.h"
|
||||||
|
#include "ns3/string.h"
|
||||||
|
#include "ns3/config.h"
|
||||||
|
|
||||||
|
#include "ns3/names.h"
|
||||||
|
#include "ns3/log.h"
|
||||||
|
#include "ns3/node.h"
|
||||||
|
#include "ns3/inet-socket-address.h"
|
||||||
|
#include "ns3/random-variable-stream.h"
|
||||||
|
|
||||||
|
#include "ns3/ipv4-l3-protocol.h"
|
||||||
|
#include "ns3/ipv4-static-routing.h"
|
||||||
|
#include "ns3/udp-socket-factory.h"
|
||||||
|
#include "ns3/udp-socket.h"
|
||||||
|
#include "ns3/internet-stack-helper.h"
|
||||||
|
#include "ns3/ipv4-list-routing-helper.h"
|
||||||
|
#include "ns3/ipv4-static-routing-helper.h"
|
||||||
|
#include "ns3/ipv4-address-helper.h"
|
||||||
|
#include "ns3/simple-net-device-helper.h"
|
||||||
|
#include "ns3/packet-sink-helper.h"
|
||||||
|
|
||||||
|
#include "ns3/traffic-control-layer.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
using namespace ns3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup internet-test
|
||||||
|
* \ingroup tests
|
||||||
|
*
|
||||||
|
* \brief IPv4 Deduplication Test
|
||||||
|
*
|
||||||
|
* Tests topology:
|
||||||
|
*
|
||||||
|
* /---- B ----\
|
||||||
|
* A ---- | ---- D ---- E
|
||||||
|
* \---- C ----/
|
||||||
|
*
|
||||||
|
* This test case counts the number of packets received
|
||||||
|
* and dropped at each node across the topology. Every
|
||||||
|
* node is configured to forward the multicast packet
|
||||||
|
* which originates at node A.
|
||||||
|
*
|
||||||
|
* With RFC 6621 de-duplication enabled, one 1 packet
|
||||||
|
* is received while some number of duplicate relayed
|
||||||
|
* packets are dropped by RFC 6621 at each node.
|
||||||
|
*
|
||||||
|
* When RFC6621 is disabled, the original packet has TTL = 4.
|
||||||
|
* Multiple packets are received at each node and several packets
|
||||||
|
* are dropped due to TTL expiry at each node.
|
||||||
|
*/
|
||||||
|
class Ipv4DeduplicationTest : public TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Send data.
|
||||||
|
* \param socket The sending socket.
|
||||||
|
* \param to Destination address.
|
||||||
|
*/
|
||||||
|
void DoSendData (Ptr<Socket> socket, std::string to);
|
||||||
|
/**
|
||||||
|
* \brief Send data.
|
||||||
|
* \param socket The sending socket.
|
||||||
|
* \param packet The packet to send.
|
||||||
|
* \param to Destination address.
|
||||||
|
*/
|
||||||
|
void DoSendPacket (Ptr<Socket> socket, Ptr<Packet> packet, std::string to);
|
||||||
|
/**
|
||||||
|
* \brief Send data.
|
||||||
|
* \param socket The sending socket.
|
||||||
|
* \param to Destination address.
|
||||||
|
*/
|
||||||
|
void SendData (Ptr<Socket> socket, std::string to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Send data.
|
||||||
|
* \param socket The sending socket.
|
||||||
|
* \param packet The packet to send.
|
||||||
|
* \param to Destination address.
|
||||||
|
*/
|
||||||
|
void SendPacket (Ptr<Socket> socket, Ptr<Packet> packet, std::string to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check packet receptions
|
||||||
|
* \param name Node name
|
||||||
|
*/
|
||||||
|
void CheckPackets (const std::string &name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check packet drops
|
||||||
|
* \param name Node name
|
||||||
|
*/
|
||||||
|
void CheckDrops (const std::string &name);
|
||||||
|
|
||||||
|
static const Time DELAY;
|
||||||
|
static std::string MakeName (bool enable, Time expire);
|
||||||
|
|
||||||
|
enum MODE {ENABLED = 0,
|
||||||
|
DISABLED,
|
||||||
|
DEGENERATE}; // enabled, but expiration time too low
|
||||||
|
MODE m_mode;
|
||||||
|
Time m_expire;
|
||||||
|
std::map<std::string, uint32_t> m_packetCountMap;
|
||||||
|
std::map<std::string, uint32_t> m_dropCountMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual void DoRun (void);
|
||||||
|
Ipv4DeduplicationTest (bool enable, Time expire = Seconds (1));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Receive data.
|
||||||
|
* \param [in] socket The receive socket.
|
||||||
|
*/
|
||||||
|
void ReceivePkt (Ptr<Socket> socket);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Register dropped packet.
|
||||||
|
* \param [in] ipHeader IP header
|
||||||
|
* \param [in] packet Packet that was dropped
|
||||||
|
* \param [in] reason Reason for dropping packet
|
||||||
|
* \param [in] ipv4 Ipv4 instance
|
||||||
|
* \param [in] interface Interface number
|
||||||
|
*/
|
||||||
|
void DropPkt (const Ipv4Header &ipHeader,
|
||||||
|
Ptr<const Packet> packet, Ipv4L3Protocol::DropReason reason,
|
||||||
|
Ptr<Ipv4> ipv4, uint32_t interface);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Time
|
||||||
|
Ipv4DeduplicationTest::DELAY = MilliSeconds (1);
|
||||||
|
|
||||||
|
Ipv4DeduplicationTest::Ipv4DeduplicationTest (bool enable, Time expire)
|
||||||
|
: TestCase (MakeName (enable, expire))
|
||||||
|
, m_mode (ENABLED)
|
||||||
|
, m_expire (expire)
|
||||||
|
{
|
||||||
|
if (!enable)
|
||||||
|
{
|
||||||
|
m_mode = DISABLED;
|
||||||
|
}
|
||||||
|
else if (m_expire < DELAY)
|
||||||
|
{
|
||||||
|
m_mode = DEGENERATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ipv4DeduplicationTest::ReceivePkt (Ptr<Socket> socket)
|
||||||
|
{
|
||||||
|
uint32_t availableData;
|
||||||
|
availableData = socket->GetRxAvailable ();
|
||||||
|
Ptr<Packet> packet = socket->Recv (std::numeric_limits<uint32_t>::max (), 0);
|
||||||
|
NS_ASSERT (availableData == packet->GetSize ());
|
||||||
|
|
||||||
|
auto node = socket->GetNode ();
|
||||||
|
std::string name = Names::FindName (node);
|
||||||
|
m_packetCountMap.insert ({name, 0}); // only inserts when not there
|
||||||
|
++m_packetCountMap[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Ipv4DeduplicationTest::DropPkt (const Ipv4Header &ipHeader,
|
||||||
|
Ptr<const Packet> packet, Ipv4L3Protocol::DropReason reason,
|
||||||
|
Ptr<Ipv4> ipv4, uint32_t interface)
|
||||||
|
{
|
||||||
|
switch (m_mode)
|
||||||
|
{
|
||||||
|
case ENABLED:
|
||||||
|
NS_TEST_EXPECT_MSG_EQ (reason, Ipv4L3Protocol::DROP_DUPLICATE, "Wrong reason for drop");
|
||||||
|
break;
|
||||||
|
case DISABLED:
|
||||||
|
NS_TEST_EXPECT_MSG_EQ (reason, Ipv4L3Protocol::DROP_TTL_EXPIRED, "Wrong reason for drop");
|
||||||
|
break;
|
||||||
|
case DEGENERATE:
|
||||||
|
// reason can be either
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
auto node = ipv4->GetNetDevice (interface)->GetNode ();
|
||||||
|
std::string name = Names::FindName (node);
|
||||||
|
m_dropCountMap.insert ({name, 0});
|
||||||
|
++m_dropCountMap[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Ipv4DeduplicationTest::DoSendData (Ptr<Socket> socket, std::string to)
|
||||||
|
{
|
||||||
|
SendPacket (socket, Create<Packet> (123), to);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Ipv4DeduplicationTest::DoSendPacket (Ptr<Socket> socket, Ptr<Packet> packet, std::string to)
|
||||||
|
{
|
||||||
|
Address realTo = InetSocketAddress (Ipv4Address (to.c_str ()), 1234);
|
||||||
|
NS_TEST_EXPECT_MSG_EQ (socket->SendTo (packet, 0, realTo), 123, "100");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Ipv4DeduplicationTest::SendData (Ptr<Socket> socket, std::string to)
|
||||||
|
{
|
||||||
|
DoSendData(socket, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Ipv4DeduplicationTest::SendPacket (Ptr<Socket> socket, Ptr<Packet> packet, std::string to)
|
||||||
|
{
|
||||||
|
Simulator::ScheduleWithContext (socket->GetNode ()->GetId (), MilliSeconds (50),
|
||||||
|
&Ipv4DeduplicationTest::DoSendPacket, this, socket, packet, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Ipv4DeduplicationTest::MakeName (bool enabled, Time expire)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "IP v4 RFC 6621 De-duplication: ";
|
||||||
|
if (!enabled)
|
||||||
|
{
|
||||||
|
oss << "disabled";
|
||||||
|
}
|
||||||
|
else if (expire > DELAY)
|
||||||
|
{
|
||||||
|
oss << "enabled";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oss << "degenerate";
|
||||||
|
}
|
||||||
|
oss << ", expire = " << expire.ToDouble (Time::MS) << "ms";
|
||||||
|
|
||||||
|
return oss.str ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Ipv4DeduplicationTest::DoRun (void)
|
||||||
|
{
|
||||||
|
// multicast target
|
||||||
|
const std::string targetAddr = "239.192.100.1";
|
||||||
|
Config::SetDefault ("ns3::Ipv4L3Protocol::EnableRFC6621", BooleanValue (m_mode != DISABLED));
|
||||||
|
Config::SetDefault ("ns3::Ipv4L3Protocol::DuplicateExpire", TimeValue (m_expire));
|
||||||
|
|
||||||
|
// Create topology
|
||||||
|
|
||||||
|
// Create nodes
|
||||||
|
auto nodes = NodeContainer ();
|
||||||
|
nodes.Create (5);
|
||||||
|
|
||||||
|
// Name nodes
|
||||||
|
Names::Add ("A", nodes.Get (0));
|
||||||
|
Names::Add ("B", nodes.Get (1));
|
||||||
|
Names::Add ("C", nodes.Get (2));
|
||||||
|
Names::Add ("D", nodes.Get (3));
|
||||||
|
Names::Add ("E", nodes.Get (4));
|
||||||
|
|
||||||
|
SimpleNetDeviceHelper simplenet;
|
||||||
|
auto devices = simplenet.Install (nodes);
|
||||||
|
// name devices
|
||||||
|
Names::Add ("A/dev", devices.Get (0));
|
||||||
|
Names::Add ("B/dev", devices.Get (1));
|
||||||
|
Names::Add ("C/dev", devices.Get (2));
|
||||||
|
Names::Add ("D/dev", devices.Get (3));
|
||||||
|
Names::Add ("E/dev", devices.Get (4));
|
||||||
|
|
||||||
|
Ipv4ListRoutingHelper listRouting;
|
||||||
|
Ipv4StaticRoutingHelper staticRouting;
|
||||||
|
listRouting.Add (staticRouting, 0);
|
||||||
|
|
||||||
|
InternetStackHelper internet;
|
||||||
|
internet.SetIpv6StackInstall (false);
|
||||||
|
internet.SetIpv4ArpJitter (true);
|
||||||
|
internet.SetRoutingHelper (listRouting);
|
||||||
|
internet.Install (nodes);
|
||||||
|
|
||||||
|
Ipv4AddressHelper ipv4address;
|
||||||
|
ipv4address.SetBase ("10.0.0.0", "255.255.255.0");
|
||||||
|
ipv4address.Assign (devices);
|
||||||
|
|
||||||
|
// add static routes for each node / device
|
||||||
|
auto diter = devices.Begin ();
|
||||||
|
for (auto end = nodes.End (),
|
||||||
|
iter = nodes.Begin (); iter != end; ++iter)
|
||||||
|
{
|
||||||
|
// route for forwarding
|
||||||
|
staticRouting.AddMulticastRoute (*iter, Ipv4Address::GetAny (), targetAddr.c_str (), *diter, NetDeviceContainer (*diter));
|
||||||
|
|
||||||
|
// route for host
|
||||||
|
// Use host routing entry according to note in Ipv4StaticRouting::RouteOutput:
|
||||||
|
//// 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 ()
|
||||||
|
auto ipv4 = (*iter)->GetObject <Ipv4> ();
|
||||||
|
NS_ASSERT_MSG ((bool) ipv4, "Node " << Names::FindName (*iter) << " does not have Ipv4 aggregate");
|
||||||
|
auto routing = staticRouting.GetStaticRouting (ipv4);
|
||||||
|
routing->AddHostRouteTo (targetAddr.c_str (), ipv4->GetInterfaceForDevice (*diter), 0);
|
||||||
|
|
||||||
|
++diter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the topology, by default fully-connected
|
||||||
|
auto channel = devices.Get (0)->GetChannel ();
|
||||||
|
auto simplechannel = channel->GetObject <SimpleChannel> ();
|
||||||
|
// ensure some time progress between re-transmissions
|
||||||
|
simplechannel->SetAttribute ("Delay", TimeValue (DELAY));
|
||||||
|
simplechannel->BlackList (Names::Find <SimpleNetDevice> ("A/dev"), Names::Find <SimpleNetDevice> ("D/dev"));
|
||||||
|
simplechannel->BlackList (Names::Find <SimpleNetDevice> ("D/dev"), Names::Find <SimpleNetDevice> ("A/dev"));
|
||||||
|
|
||||||
|
simplechannel->BlackList (Names::Find <SimpleNetDevice> ("A/dev"), Names::Find <SimpleNetDevice> ("E/dev"));
|
||||||
|
simplechannel->BlackList (Names::Find <SimpleNetDevice> ("E/dev"), Names::Find <SimpleNetDevice> ("A/dev"));
|
||||||
|
|
||||||
|
simplechannel->BlackList (Names::Find <SimpleNetDevice> ("B/dev"), Names::Find <SimpleNetDevice> ("E/dev"));
|
||||||
|
simplechannel->BlackList (Names::Find <SimpleNetDevice> ("E/dev"), Names::Find <SimpleNetDevice> ("B/dev"));
|
||||||
|
|
||||||
|
simplechannel->BlackList (Names::Find <SimpleNetDevice> ("C/dev"), Names::Find <SimpleNetDevice> ("E/dev"));
|
||||||
|
simplechannel->BlackList (Names::Find <SimpleNetDevice> ("E/dev"), Names::Find <SimpleNetDevice> ("C/dev"));
|
||||||
|
|
||||||
|
// Create the UDP sockets
|
||||||
|
std::list<Ptr<Socket> > sockets;
|
||||||
|
for (auto end = nodes.End (),
|
||||||
|
iter = nodes.Begin (); iter != end; ++iter)
|
||||||
|
{
|
||||||
|
auto SocketFactory = (*iter)->GetObject<UdpSocketFactory> ();
|
||||||
|
auto socket = SocketFactory->CreateSocket ();
|
||||||
|
socket->SetAllowBroadcast (true);
|
||||||
|
NS_TEST_EXPECT_MSG_EQ (socket->Bind (InetSocketAddress (Ipv4Address::GetAny (), 1234)), 0,
|
||||||
|
"Could not bind socket for node " << Names::FindName (*iter));
|
||||||
|
socket->SetRecvCallback (MakeCallback (&Ipv4DeduplicationTest::ReceivePkt, this));
|
||||||
|
sockets.push_back (socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect up drop traces
|
||||||
|
Config::ConnectWithoutContext ("/NodeList/*/$ns3::Ipv4L3Protocol/Drop",
|
||||||
|
MakeCallback (&Ipv4DeduplicationTest::DropPkt, this));
|
||||||
|
|
||||||
|
// start TX from A
|
||||||
|
auto txSocket = sockets.front ();
|
||||||
|
auto udpSocket = txSocket->GetObject<UdpSocket> ();
|
||||||
|
udpSocket->MulticastJoinGroup (0, Ipv4Address (targetAddr.c_str ())); // future proof?
|
||||||
|
udpSocket->SetAttribute ("IpMulticastTtl", StringValue ("4"));
|
||||||
|
|
||||||
|
// ------ Now the tests ------------
|
||||||
|
|
||||||
|
// Broadcast 1 packet
|
||||||
|
SendData (txSocket, targetAddr);
|
||||||
|
Simulator::Run ();
|
||||||
|
Simulator::Destroy ();
|
||||||
|
|
||||||
|
for (auto end = nodes.End (),
|
||||||
|
iter = nodes.Begin (); iter != end; ++iter)
|
||||||
|
{
|
||||||
|
std::string name = Names::FindName (*iter);
|
||||||
|
CheckPackets (name);
|
||||||
|
CheckDrops (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_packetCountMap.clear ();
|
||||||
|
sockets.clear ();
|
||||||
|
Names::Clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// The de-duplicate disabled received packets and drops can be
|
||||||
|
// computed by forming the connectivity matrix C with 1's in
|
||||||
|
// coordinates (row, column) where row and column nodes are connected.
|
||||||
|
// Reception of packets with TTL n are v_(n-1) = v_n * C where
|
||||||
|
// v_TTL = [1 0 0 0 0] (corresponding to nodes [A B C D E]).
|
||||||
|
// The number of drops for each node is v_0 and the number of received
|
||||||
|
// packets at each node is sum (v_TTL-1, ..., v_0).
|
||||||
|
void
|
||||||
|
Ipv4DeduplicationTest::CheckPackets (const std::string &name)
|
||||||
|
{
|
||||||
|
// a priori determined packet receptions based on initial TTL of 4, disabled de-dup
|
||||||
|
std::map <std::string, uint32_t> packets = {
|
||||||
|
{"A", 14}, {"B", 16}, {"C", 16}, {"D", 16}, {"E", 4}
|
||||||
|
};
|
||||||
|
// a priori determined packet receptions based on initial TTL of 4, degenerate de-dup
|
||||||
|
// There are TTL (4) rounds of packets. Each round a node will register a
|
||||||
|
// received packet if another connected node transmits. A misses the 1st round
|
||||||
|
// since it is the only one transmitting. D is not connected to A in 1st round
|
||||||
|
// either. E only hears a packet in the 3rd and 4th rounds.
|
||||||
|
std::map <std::string, uint32_t> degenerates = {
|
||||||
|
{"A", 3}, {"B", 4}, {"C", 4}, {"D", 3}, {"E", 2}
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_TEST_ASSERT_MSG_NE ((m_packetCountMap.find (name) == m_packetCountMap.end ()), true,
|
||||||
|
"No packets received for node " << name);
|
||||||
|
switch (m_mode)
|
||||||
|
{
|
||||||
|
case ENABLED:
|
||||||
|
NS_TEST_EXPECT_MSG_EQ (m_packetCountMap[name], 1, "Wrong number of packets received for node " << name);
|
||||||
|
break;
|
||||||
|
case DISABLED:
|
||||||
|
NS_TEST_EXPECT_MSG_EQ (m_packetCountMap[name], packets[name], "Wrong number of packets received for node " << name);
|
||||||
|
break;
|
||||||
|
case DEGENERATE:
|
||||||
|
NS_TEST_EXPECT_MSG_EQ (m_packetCountMap[name], degenerates[name], "Wrong number of packets received for node " << name);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Ipv4DeduplicationTest::CheckDrops (const std::string &name)
|
||||||
|
{
|
||||||
|
std::map <std::string, uint32_t> drops;
|
||||||
|
switch (m_mode)
|
||||||
|
{
|
||||||
|
case ENABLED:
|
||||||
|
// a priori determined packet drops based on initial TTL of 4, enabled de-dup;
|
||||||
|
// A hears from B & C
|
||||||
|
// D hears from B, C, AND E
|
||||||
|
// B (C) hears from A, C (B), D, and A again
|
||||||
|
drops = {{"A", 1}, {"B", 3}, {"C", 3}, {"D", 2}, {"E", 0}};
|
||||||
|
break;
|
||||||
|
case DISABLED:
|
||||||
|
// a priori determined packet drops based on initial TTL of 4, disabled de-dup
|
||||||
|
drops = {{"A", 10}, {"B", 9}, {"C", 9}, {"D", 12}, {"E", 2}};
|
||||||
|
break;
|
||||||
|
case DEGENERATE:
|
||||||
|
// a priori determined packet drops based on initial TTL of 4, degenerate de-dup
|
||||||
|
// There are TTL (4) rounds of transmissions. Since all transmitters are
|
||||||
|
// synchronized, multiple packets are received each round when there are
|
||||||
|
// multiple transmitters. Only 1 packet per round is delivered, others are
|
||||||
|
// dropped. So this can be computed via "disabled" procedure described
|
||||||
|
// in check packets, but with only a 1 for each node in each round when packets
|
||||||
|
// are received. Drops are the sum of receptions using these indicator receive vectors
|
||||||
|
// subtracting 1 for each node (for the delivered packet) and adding 1
|
||||||
|
// at all nodes for TTL expiry.
|
||||||
|
drops = {{"A", 4}, {"B", 5}, {"C", 5}, {"D", 5}, {"E", 1}};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drops[name])
|
||||||
|
{
|
||||||
|
NS_TEST_ASSERT_MSG_NE ((m_dropCountMap.find (name) == m_dropCountMap.end ()), true, "No drops for node " << name);
|
||||||
|
NS_TEST_EXPECT_MSG_EQ (m_dropCountMap[name], drops[name], "Wrong number of drops for node " << name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NS_TEST_EXPECT_MSG_EQ ((m_dropCountMap.find (name) == m_dropCountMap.end ()), true, "Non-0 drops for node " << name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup internet-test
|
||||||
|
* \ingroup tests
|
||||||
|
*
|
||||||
|
* \brief IPv4 Deduplication TestSuite
|
||||||
|
*/
|
||||||
|
class Ipv4DeduplicationTestSuite : public TestSuite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Ipv4DeduplicationTestSuite ();
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
Ipv4DeduplicationTestSuite::Ipv4DeduplicationTestSuite ()
|
||||||
|
: TestSuite ("ipv4-deduplication", UNIT)
|
||||||
|
{
|
||||||
|
|
||||||
|
AddTestCase (new Ipv4DeduplicationTest (true), TestCase::QUICK);
|
||||||
|
AddTestCase (new Ipv4DeduplicationTest (false), TestCase::QUICK);
|
||||||
|
// degenerate case is enabled RFC but with too short an expiry
|
||||||
|
AddTestCase (new Ipv4DeduplicationTest (true, MicroSeconds (50)), TestCase::QUICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Ipv4DeduplicationTestSuite g_ipv4DeduplicationTestSuite; //!< Static variable for test initialization
|
||||||
|
|
||||||
@@ -297,6 +297,7 @@ def build(bld):
|
|||||||
'test/ipv4-rip-test.cc',
|
'test/ipv4-rip-test.cc',
|
||||||
'test/tcp-close-test.cc',
|
'test/tcp-close-test.cc',
|
||||||
'test/icmp-test.cc',
|
'test/icmp-test.cc',
|
||||||
|
'test/ipv4-deduplication-test.cc',
|
||||||
]
|
]
|
||||||
privateheaders = bld(features='ns3privateheader')
|
privateheaders = bld(features='ns3privateheader')
|
||||||
privateheaders.module = 'internet'
|
privateheaders.module = 'internet'
|
||||||
|
|||||||
Reference in New Issue
Block a user