model: add queue length congestion detection and signaling

add PCON consumer app

refs #4578

Change-Id: I1b04e3d123510b1ed6134a708c2710ebbb217167
diff --git a/apps/ndn-consumer-pcon.cpp b/apps/ndn-consumer-pcon.cpp
new file mode 100644
index 0000000..3add082
--- /dev/null
+++ b/apps/ndn-consumer-pcon.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2011-2018  Regents of the University of California.
+ *
+ * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
+ * contributors.
+ *
+ * ndnSIM is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * ndnSIM 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
+ * ndnSIM, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "ndn-consumer-pcon.hpp"
+
+NS_LOG_COMPONENT_DEFINE("ndn.ConsumerPcon");
+
+namespace ns3 {
+namespace ndn {
+
+NS_OBJECT_ENSURE_REGISTERED(ConsumerPcon);
+
+TypeId
+ConsumerPcon::GetTypeId()
+{
+  static TypeId tid =
+    TypeId("ns3::ndn::ConsumerPcon")
+      .SetGroupName("Ndn")
+      .SetParent<ConsumerWindow>()
+      .AddConstructor<ConsumerPcon>()
+
+      .AddAttribute("Beta", "TCP Multiplicative Decrease factor",
+                    DoubleValue(0.5),
+                    MakeDoubleAccessor(&ConsumerPcon::m_beta),
+                    MakeDoubleChecker<double>())
+
+      .AddAttribute("AddRttSupress", "Minimum number of RTTs (1 + this factor) between window decreases",
+                    DoubleValue(0.5), // This default value was chosen after manual testing
+                    MakeDoubleAccessor(&ConsumerPcon::m_addRttSupress),
+                    MakeDoubleChecker<double>())
+
+      .AddAttribute("ShouldReactToCongestionMarks", "If true, process received congestion marks",
+                    BooleanValue(true),
+                    MakeBooleanAccessor(&ConsumerPcon::m_shouldReactToCongestionMarks),
+                    MakeBooleanChecker())
+
+      .AddAttribute("ShouldUseCwa", "If true, use Conservative Window Adaptation",
+                    BooleanValue(true),
+                    MakeBooleanAccessor(&ConsumerPcon::m_shouldUseCwa),
+                    MakeBooleanChecker());
+
+  return tid;
+}
+
+ConsumerPcon::ConsumerPcon()
+  : m_ssthresh(std::numeric_limits<double>::max())
+  , m_highData(0)
+  , m_recPoint(0.0)
+{
+}
+
+void
+ConsumerPcon::OnData(shared_ptr<const Data> data)
+{
+  Consumer::OnData(data);
+
+  uint64_t sequenceNum = data->getName().get(-1).toSegment();
+
+  // Set highest received Data to sequence number
+  if (m_highData < sequenceNum) {
+    m_highData = sequenceNum;
+  }
+
+  if (data->getCongestionMark() > 0) {
+    if (m_shouldReactToCongestionMarks) {
+      NS_LOG_DEBUG("Received congestion mark: " << data->getCongestionMark());
+      WindowDecrease();
+    }
+    else {
+      NS_LOG_DEBUG("Ignored received congestion mark: " << data->getCongestionMark());
+    }
+  }
+  else {
+    WindowIncrease();
+  }
+
+  if (m_inFlight > static_cast<uint32_t>(0)) {
+    m_inFlight--;
+  }
+
+  NS_LOG_DEBUG("Window: " << m_window << ", InFlight: " << m_inFlight);
+
+  ScheduleNextPacket();
+}
+
+void
+ConsumerPcon::OnTimeout(uint32_t sequenceNum)
+{
+  WindowDecrease();
+
+  if (m_inFlight > static_cast<uint32_t>(0)) {
+    m_inFlight--;
+  }
+
+  NS_LOG_DEBUG("Window: " << m_window << ", InFlight: " << m_inFlight);
+
+  Consumer::OnTimeout(sequenceNum);
+}
+
+void
+ConsumerPcon::WindowIncrease()
+{
+  if (m_window < m_ssthresh) {
+    m_window += 1.0;
+  }
+  else {
+    m_window += (1.0 / m_window);
+  }
+
+  NS_LOG_DEBUG("Window size increased to " << m_window);
+}
+
+void
+ConsumerPcon::WindowDecrease()
+{
+  if (m_shouldUseCwa || m_highData > m_recPoint) {
+    const double diff = m_seq - m_highData;
+    assert(diff > 0);
+
+    m_recPoint = m_seq + (m_addRttSupress * diff);
+
+    m_ssthresh = m_window * m_beta;
+    m_window = m_ssthresh;
+
+    // Window size cannot be reduced below initial size
+    if (m_window < m_initialWindow) {
+      m_window = m_initialWindow;
+    }
+
+    NS_LOG_DEBUG("Window size decreased to " << m_window);
+  }
+  else {
+    NS_LOG_DEBUG("Window decrease suppressed, HighData: " << m_highData << ", RecPoint: " << m_recPoint);
+  }
+}
+
+} // namespace ndn
+} // namespace ns3
diff --git a/apps/ndn-consumer-pcon.hpp b/apps/ndn-consumer-pcon.hpp
new file mode 100644
index 0000000..9600829
--- /dev/null
+++ b/apps/ndn-consumer-pcon.hpp
@@ -0,0 +1,70 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2011-2018  Regents of the University of California.
+ *
+ * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
+ * contributors.
+ *
+ * ndnSIM is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * ndnSIM 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
+ * ndnSIM, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef NDN_CONSUMER_PCON_H
+#define NDN_CONSUMER_PCON_H
+
+#include "ns3/ndnSIM/model/ndn-common.hpp"
+
+#include "ndn-consumer-window.hpp"
+
+namespace ns3 {
+namespace ndn {
+
+/**
+ * @ingroup ndn-apps
+ * \brief NDN consumer application (uses a PCON congestion window)
+ *
+ * Please refer to ConsumerWindow for further documentation on how to use this consumer.
+ */
+class ConsumerPcon : public ConsumerWindow {
+public:
+  static TypeId
+  GetTypeId();
+
+  ConsumerPcon();
+
+  virtual void
+  OnData(shared_ptr<const Data> data) override;
+
+  virtual void
+  OnTimeout(uint32_t sequenceNum) override;
+
+private:
+  void
+  WindowIncrease();
+
+  void
+  WindowDecrease();
+
+private:
+  double m_beta;
+  double m_addRttSupress;
+  bool m_shouldReactToCongestionMarks;
+  bool m_shouldUseCwa;
+
+  double m_ssthresh;
+  uint32_t m_highData;
+  double m_recPoint;
+};
+
+} // namespace ndn
+} // namespace ns3
+
+#endif // NDN_CONSUMER_PCON_H
diff --git a/apps/ndn-consumer-window.hpp b/apps/ndn-consumer-window.hpp
index 4e76c50..c298e17 100644
--- a/apps/ndn-consumer-window.hpp
+++ b/apps/ndn-consumer-window.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2011-2015  Regents of the University of California.
+/*
+ * Copyright (c) 2011-2018  Regents of the University of California.
  *
  * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
  * contributors.
@@ -33,7 +33,7 @@
  * \brief Ndn application for sending out Interest packets (window-based)
  *
  * !!! ATTENTION !!! This is highly experimental and relies on experimental features of the
- *simulator.
+ * simulator.
  * Behavior may be unpredictable if used incorrectly.
  */
 class ConsumerWindow : public Consumer {
@@ -57,7 +57,7 @@
   WillSendOutInterest(uint32_t sequenceNumber);
 
 public:
-  typedef void (*WindowTraceCallback)(uint32_t);
+  typedef std::function<void(double)> WindowTraceCallback;
 
 protected:
   /**
@@ -92,18 +92,18 @@
   void
   SetSeqMax(uint32_t seqMax);
 
-private:
+protected:
   uint32_t m_payloadSize; // expected payload size
   double m_maxSize;       // max size to request
 
   uint32_t m_initialWindow;
   bool m_setInitialWindowOnTimeout;
 
-  TracedValue<uint32_t> m_window;
+  TracedValue<double> m_window;
   TracedValue<uint32_t> m_inFlight;
 };
 
 } // namespace ndn
 } // namespace ns3
 
-#endif
+#endif // NDN_CONSUMER_WINDOW_H
diff --git a/docs/source/applications.rst b/docs/source/applications.rst
index 8bb508d..c35440e 100644
--- a/docs/source/applications.rst
+++ b/docs/source/applications.rst
@@ -163,6 +163,46 @@
 
   If ``Size`` is set to -1, Interests will be requested till the end of the simulation.
 
+ConsumerPcon
+^^^^^^^^^^^^^^^^
+
+:ndnsim:`ConsumerPcon` is an application generating variable rate Interest traffic. It implements a sliding-window-based Interest generation mechanism that adjusts window size using the PCON congestion control mechanism developed by K. Schneider et al. (https://named-data.net/publications/practical_congestion_control_scheme/). It is derived from :ndnsim:`ConsumerWindow`.
+
+.. code-block:: c++
+
+    // Create application using the app helper
+    AppHelper consumerHelper("ns3::ndn::ConsumerPcon");
+
+The application has the same attributes as :ndnsim:`ConsumerWindow`, in addition to the following:
+
+* ``Beta``
+
+  .. note::
+     default: ``0.5``
+
+  TCP Multiplicative Decrease factor
+
+* ``AddRttSupress``
+
+  .. note::
+     default: ``0.5``
+
+  Minimum number of RTTs (1 + this factor) between window decreases
+
+* ``ShouldReactToCongestionMarks``
+
+  .. note::
+     default: ``true``
+
+  If true, process received congestion marks; otherwise, ignore them
+
+* ``ShouldUseCwa``
+
+  .. note::
+     default: ``true``
+
+  If true, use Conservative Window Adaptation
+
 Producer
 ^^^^^^^^^^^^
 
diff --git a/model/ndn-net-device-transport.cpp b/model/ndn-net-device-transport.cpp
index 32abaa6..17880d2 100644
--- a/model/ndn-net-device-transport.cpp
+++ b/model/ndn-net-device-transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2011-2016  Regents of the University of California.
+/*
+ * Copyright (c) 2011-2018  Regents of the University of California.
  *
  * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
  * contributors.
@@ -27,6 +27,8 @@
 #include <ndn-cxx/interest.hpp>
 #include <ndn-cxx/data.hpp>
 
+#include "ns3/queue.h"
+
 NS_LOG_COMPONENT_DEFINE("ndn.NetDeviceTransport");
 
 namespace ns3 {
@@ -49,6 +51,14 @@
   this->setLinkType(linkType);
   this->setMtu(m_netDevice->GetMtu()); // Use the MTU of the netDevice
 
+  // Get send queue capacity for congestion marking
+  PointerValue txQueueAttribute;
+  if (m_netDevice->GetAttributeFailSafe("TxQueue", txQueueAttribute)) {
+    Ptr<ns3::QueueBase> txQueue = txQueueAttribute.Get<ns3::QueueBase>();
+    // must be put into bytes mode queue
+    this->setSendQueueCapacity(txQueue->GetMaxBytes());
+  }
+
   NS_LOG_FUNCTION(this << "Creating an ndnSIM transport instance for netDevice with URI"
                   << this->getLocalUri());
 
@@ -64,6 +74,19 @@
   NS_LOG_FUNCTION_NOARGS();
 }
 
+ssize_t
+NetDeviceTransport::getSendQueueLength()
+{
+  PointerValue txQueueAttribute;
+  if (m_netDevice->GetAttributeFailSafe("TxQueue", txQueueAttribute)) {
+    Ptr<ns3::QueueBase> txQueue = txQueueAttribute.Get<ns3::QueueBase>();
+    return txQueue->GetNBytes();
+  }
+  else {
+    return nfd::face::QUEUE_UNSUPPORTED;
+  }
+}
+
 void
 NetDeviceTransport::doClose()
 {
diff --git a/model/ndn-net-device-transport.hpp b/model/ndn-net-device-transport.hpp
index 09a08ed..e68382d 100644
--- a/model/ndn-net-device-transport.hpp
+++ b/model/ndn-net-device-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2011-2016  Regents of the University of California.
+/*
+ * Copyright (c) 2011-2018  Regents of the University of California.
  *
  * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
  * contributors.
@@ -54,6 +54,9 @@
   Ptr<NetDevice>
   GetNetDevice() const;
 
+  virtual ssize_t
+  getSendQueueLength() final;
+
 private:
   virtual void
   doClose() override;