From 60dc6f91b9320b35657b762a3567513a5957a82e Mon Sep 17 00:00:00 2001 From: Jared Dulmage Date: Sat, 26 Oct 2019 13:16:55 +0200 Subject: [PATCH] internet: (Merge !102) Add RFC6621 IPv4 layer de-duplication --- src/internet/model/ipv4-l3-protocol.cc | 110 +++- src/internet/model/ipv4-l3-protocol.h | 26 +- src/internet/test/ipv4-deduplication-test.cc | 500 +++++++++++++++++++ src/internet/wscript | 1 + 4 files changed, 635 insertions(+), 2 deletions(-) create mode 100644 src/internet/test/ipv4-deduplication-test.cc diff --git a/src/internet/model/ipv4-l3-protocol.cc b/src/internet/model/ipv4-l3-protocol.cc index 6e5f667d7..98fb54200 100644 --- a/src/internet/model/ipv4-l3-protocol.cc +++ b/src/internet/model/ipv4-l3-protocol.cc @@ -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 device, Ptr 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 (), 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 key, Ipv4H m_fragments.erase (key); m_fragmentsTimers.erase (key); } + +bool +Ipv4L3Protocol::UpdateDuplicate (Ptr 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 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 diff --git a/src/internet/model/ipv4-l3-protocol.h b/src/internet/model/ipv4-l3-protocol.h index ed147e9ad..e4d2b443d 100644 --- a/src/internet/model/ipv4-l3-protocol.h +++ b/src/internet/model/ipv4-l3-protocol.h @@ -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 DupTuple_t; + /// Maps packet duplicate tuple to expiration event + typedef std::map 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 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 diff --git a/src/internet/test/ipv4-deduplication-test.cc b/src/internet/test/ipv4-deduplication-test.cc new file mode 100644 index 000000000..d8d6cf832 --- /dev/null +++ b/src/internet/test/ipv4-deduplication-test.cc @@ -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 + * Modified (2019): Jared Dulmage + * 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 +#include +#include + +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, 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, Ptr packet, std::string to); + /** + * \brief Send data. + * \param socket The sending socket. + * \param to Destination address. + */ + void SendData (Ptr 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, Ptr 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 m_packetCountMap; + std::map 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); + + /** + * \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 packet, Ipv4L3Protocol::DropReason reason, + Ptr 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) +{ + uint32_t availableData; + availableData = socket->GetRxAvailable (); + Ptr packet = socket->Recv (std::numeric_limits::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 packet, Ipv4L3Protocol::DropReason reason, + Ptr 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, std::string to) +{ + SendPacket (socket, Create (123), to); +} + +void +Ipv4DeduplicationTest::DoSendPacket (Ptr socket, Ptr 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, std::string to) +{ + DoSendData(socket, to); +} + +void +Ipv4DeduplicationTest::SendPacket (Ptr socket, Ptr 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 (); + 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 (); + // ensure some time progress between re-transmissions + simplechannel->SetAttribute ("Delay", TimeValue (DELAY)); + simplechannel->BlackList (Names::Find ("A/dev"), Names::Find ("D/dev")); + simplechannel->BlackList (Names::Find ("D/dev"), Names::Find ("A/dev")); + + simplechannel->BlackList (Names::Find ("A/dev"), Names::Find ("E/dev")); + simplechannel->BlackList (Names::Find ("E/dev"), Names::Find ("A/dev")); + + simplechannel->BlackList (Names::Find ("B/dev"), Names::Find ("E/dev")); + simplechannel->BlackList (Names::Find ("E/dev"), Names::Find ("B/dev")); + + simplechannel->BlackList (Names::Find ("C/dev"), Names::Find ("E/dev")); + simplechannel->BlackList (Names::Find ("E/dev"), Names::Find ("C/dev")); + + // Create the UDP sockets + std::list > sockets; + for (auto end = nodes.End (), + iter = nodes.Begin (); iter != end; ++iter) + { + auto SocketFactory = (*iter)->GetObject (); + 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->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 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 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 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 + diff --git a/src/internet/wscript b/src/internet/wscript index ed19722e5..68a97f4e9 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -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'