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/net-device.h"
|
||||
#include "ns3/uinteger.h"
|
||||
#include "ns3/string.h"
|
||||
#include "ns3/boolean.h"
|
||||
#include "ns3/trace-source-accessor.h"
|
||||
#include "ns3/object-vector.h"
|
||||
#include "ns3/ipv4-header.h"
|
||||
@@ -69,6 +71,15 @@ Ipv4L3Protocol::GetTypeId (void)
|
||||
TimeValue (Seconds (30)),
|
||||
MakeTimeAccessor (&Ipv4L3Protocol::m_fragmentExpirationTimeout),
|
||||
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",
|
||||
"Send ipv4 packet to outgoing interface.",
|
||||
MakeTraceSourceAccessor (&Ipv4L3Protocol::m_txTrace),
|
||||
@@ -318,6 +329,15 @@ Ipv4L3Protocol::DoDispose (void)
|
||||
m_fragments.clear ();
|
||||
m_fragmentsTimers.clear ();
|
||||
|
||||
for (auto dup: m_dups)
|
||||
{
|
||||
if (dup.second.IsRunning ())
|
||||
{
|
||||
dup.second.Cancel ();
|
||||
}
|
||||
}
|
||||
m_dups.clear ();
|
||||
|
||||
Object::DoDispose ();
|
||||
}
|
||||
|
||||
@@ -506,7 +526,7 @@ Ipv4L3Protocol::IsDestinationAddress (Ipv4Address address, uint32_t iif) const
|
||||
#endif
|
||||
if (true)
|
||||
{
|
||||
NS_LOG_LOGIC ("For me (Ipv4Addr multicast address");
|
||||
NS_LOG_LOGIC ("For me (Ipv4Addr multicast address)");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -629,6 +649,13 @@ Ipv4L3Protocol::Receive ( Ptr<NetDevice> device, Ptr<const Packet> p, uint16_t p
|
||||
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");
|
||||
if (!m_routingProtocol->RouteInput (packet, ipHeader, device,
|
||||
MakeCallback (&Ipv4L3Protocol::IpForward, this),
|
||||
@@ -1697,4 +1724,85 @@ Ipv4L3Protocol::HandleFragmentsTimeout (std::pair<uint64_t, uint32_t> key, Ipv4H
|
||||
m_fragments.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
|
||||
|
||||
@@ -101,7 +101,8 @@ public:
|
||||
DROP_BAD_CHECKSUM, /**< Bad checksum */
|
||||
DROP_INTERFACE_DOWN, /**< Interface is down so can not send packet */
|
||||
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
|
||||
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
|
||||
|
||||
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/tcp-close-test.cc',
|
||||
'test/icmp-test.cc',
|
||||
'test/ipv4-deduplication-test.cc',
|
||||
]
|
||||
privateheaders = bld(features='ns3privateheader')
|
||||
privateheaders.module = 'internet'
|
||||
|
||||
Reference in New Issue
Block a user