From 204bb0d30e5c4e71e63555dea92ed857bba54b0e Mon Sep 17 00:00:00 2001 From: Jared Dulmage Date: Fri, 29 Nov 2019 23:38:12 +0100 Subject: [PATCH] internet: (merge !129) Reduced event generation for Ipv4L3Protocol multicast de-duplication --- src/internet/model/ipv4-l3-protocol.cc | 76 +++++++---- src/internet/model/ipv4-l3-protocol.h | 15 ++- src/internet/test/ipv4-deduplication-test.cc | 132 ++++++++++++++++++- 3 files changed, 190 insertions(+), 33 deletions(-) diff --git a/src/internet/model/ipv4-l3-protocol.cc b/src/internet/model/ipv4-l3-protocol.cc index a5446aea7..5b5c227ec 100644 --- a/src/internet/model/ipv4-l3-protocol.cc +++ b/src/internet/model/ipv4-l3-protocol.cc @@ -80,6 +80,12 @@ Ipv4L3Protocol::GetTypeId (void) 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), @@ -329,12 +335,9 @@ Ipv4L3Protocol::DoDispose (void) m_fragments.clear (); m_fragmentsTimers.clear (); - for (auto dup: m_dups) + if (m_cleanDpd.IsRunning ()) { - if (dup.second.IsRunning ()) - { - dup.second.Cancel (); - } + m_cleanDpd.Cancel (); } m_dups.clear (); @@ -1763,14 +1766,21 @@ Ipv4L3Protocol::UpdateDuplicate (Ptr p, const Ipv4Header &header) 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); - + { + 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.IsRunning () && m_purge.IsStrictlyPositive ()) + { + m_cleanDpd = Simulator::Schedule (m_expire, &Ipv4L3Protocol::RemoveDuplicates, this); + } + // 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) << ", " << @@ -1779,29 +1789,49 @@ Ipv4L3Protocol::UpdateDuplicate (Ptr p, const Ipv4Header &header) 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 - } + DupMap_t::iterator iter; + bool inserted, isDup; + std::tie (iter, inserted) = m_dups.emplace (key, Seconds (0)); + isDup = !inserted && iter->second > Simulator::Now (); + // set the expiration event - iter->second = Simulator::Schedule (m_expire, &Ipv4L3Protocol::RemoveDuplicate, this, iter); + iter->second = Simulator::Now () + m_expire; return isDup; } void -Ipv4L3Protocol::RemoveDuplicate (DupMap_t::const_iterator iter) +Ipv4L3Protocol::RemoveDuplicates (void) { 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); + 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); + } } diff --git a/src/internet/model/ipv4-l3-protocol.h b/src/internet/model/ipv4-l3-protocol.h index cf9194006..fd0cc76ae 100644 --- a/src/internet/model/ipv4-l3-protocol.h +++ b/src/internet/model/ipv4-l3-protocol.h @@ -560,8 +560,8 @@ private: /// 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; + /// Maps packet duplicate tuple to expiration time + typedef std::map DupMap_t; /** * Registers duplicate entry, return false if new @@ -574,12 +574,13 @@ private: * Remove duplicate packet entry * \param [in] iter Iterator into duplicate map to remove */ - void RemoveDuplicate (DupMap_t::const_iterator iter); - - bool m_enableDpd; //!< Enable multicast duplicate packet detection - DupMap_t m_dups; //!< map of packet duplicate tuples to expiry event - Time m_expire; //!< duplicate entry expiration delay + void RemoveDuplicates (void); + bool m_enableDpd; //!< Enable multicast duplicate packet detection + DupMap_t m_dups; //!< map of packet duplicate tuples to expiry event + Time m_expire; //!< duplicate entry expiration delay + Time m_purge; //!< time between purging expired duplicate entries + EventId m_cleanDpd; //!< event to cleanup expired duplicate entries }; } // Namespace ns3 diff --git a/src/internet/test/ipv4-deduplication-test.cc b/src/internet/test/ipv4-deduplication-test.cc index aefb25852..3a5f33b1f 100644 --- a/src/internet/test/ipv4-deduplication-test.cc +++ b/src/internet/test/ipv4-deduplication-test.cc @@ -31,6 +31,7 @@ #include "ns3/socket.h" #include "ns3/boolean.h" #include "ns3/double.h" +#include "ns3/uinteger.h" #include "ns3/string.h" #include "ns3/config.h" @@ -49,6 +50,7 @@ #include "ns3/ipv4-static-routing-helper.h" #include "ns3/ipv4-address-helper.h" #include "ns3/simple-net-device-helper.h" +#include "ns3/on-off-helper.h" #include "ns3/traffic-control-layer.h" @@ -356,6 +358,11 @@ Ipv4DeduplicationTest::DoRun (void) socket->SetAllowBroadcast (true); NS_TEST_EXPECT_MSG_EQ (socket->Bind (InetSocketAddress (Ipv4Address::GetAny (), 1234)), 0, "Could not bind socket for node " << Names::FindName (*iter)); + + auto udpSocket = socket->GetObject (); + udpSocket->MulticastJoinGroup (0, Ipv4Address (targetAddr.c_str ())); // future proof? + udpSocket->SetAttribute ("IpMulticastTtl", StringValue ("4")); + socket->SetRecvCallback (MakeCallback (&Ipv4DeduplicationTest::ReceivePkt, this)); sockets.push_back (socket); } @@ -366,9 +373,6 @@ Ipv4DeduplicationTest::DoRun (void) // 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 ------------ @@ -497,3 +501,125 @@ Ipv4DeduplicationTestSuite::Ipv4DeduplicationTestSuite () static Ipv4DeduplicationTestSuite g_ipv4DeduplicationTestSuite; //!< Static variable for test initialization + + +/** + * \ingroup internet-test + * \ingroup tests + * + * \brief IPv4 Deduplication Performance Test + * + * This test case sets up a fully connected network of + * 10 nodes. Each node transmits 2 packets / second + * for about 20 seconds. Packets are relayed from + * every receiver. The test outputs the number of + * events that have been processed during the course + * of the simulation. Test runtime is also a metric. + * + * The de-duplication cache entry expiration algorithm + * has evolved from an event-per-expiry (EPE) algorithm to + * a periodic event, batch purge (PBP) algorithm. The + * current metrics are taken from tests on the development + * box. Periodic batch purge period defaults to 1s. + * + * Events Runtime + * EVE 656140 29s + * PBP 337420 29s + * + */ +class Ipv4DeduplicationPerformanceTest : public TestCase +{ + public: + Ipv4DeduplicationPerformanceTest (void); + virtual void DoRun (void); +}; + +Ipv4DeduplicationPerformanceTest::Ipv4DeduplicationPerformanceTest () + : TestCase ("Ipv4Deduplication performance test") +{} + +void +Ipv4DeduplicationPerformanceTest::DoRun (void) +{ + // multicast target + const std::string targetAddr = "239.192.100.1"; + Config::SetDefault ("ns3::Ipv4L3Protocol::EnableDuplicatePacketDetection", BooleanValue (true)); + Config::SetDefault ("ns3::Ipv4L3Protocol::DuplicateExpire", TimeValue (Time ("10s"))); + + // Create nodes + auto nodes = NodeContainer (); + nodes.Create (20); + + SimpleNetDeviceHelper simplenet; + auto devices = simplenet.Install (nodes); + + 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 " << (*iter)->GetId () << " does not have Ipv4 aggregate"); + auto routing = staticRouting.GetStaticRouting (ipv4); + routing->AddHostRouteTo (targetAddr.c_str (), ipv4->GetInterfaceForDevice (*diter), 0); + + ++diter; + } + + // Create application + OnOffHelper onoff ("ns3::UdpSocketFactory", InetSocketAddress (targetAddr.c_str (), 1234)); + onoff.SetConstantRate (DataRate ("8kbps")); // 2 packets / second for default 512B packets + onoff.SetAttribute ("MaxBytes", UintegerValue (512 * 40)); // 20 seconds worth of packets + auto apps = onoff.Install (nodes); + + apps.StartWithJitter (Seconds (4), CreateObjectWithAttributes ("Max", DoubleValue (4))); + + Simulator::Run (); + NS_LOG_UNCOND ("Executed " << Simulator::GetEventCount () << " events"); + Simulator::Destroy (); +} + +/** + * \ingroup internet-test + * \ingroup tests + * + * \brief IPv4 Deduplication Performance TestSuite + */ +class Ipv4DeduplicationPerformanceTestSuite : public TestSuite +{ +public: + Ipv4DeduplicationPerformanceTestSuite (); +}; + +Ipv4DeduplicationPerformanceTestSuite::Ipv4DeduplicationPerformanceTestSuite () + : TestSuite ("ipv4-deduplication-performance", PERFORMANCE) +{ + AddTestCase (new Ipv4DeduplicationPerformanceTest, TestCase::EXTENSIVE); +} + +static Ipv4DeduplicationPerformanceTestSuite g_ipv4DeduplicationPerformanceTestSuite; //!< Static variable for test initialization