Add Congestion Mark scenario

refs #4327

Change-Id: Iabbbe241585378e77d792c74d005a22b0d1440c5
diff --git a/.gitignore b/.gitignore
index 0bb0587..c26d822 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,8 @@
 *.pyo
 /.vagrant/
 /install_helpers/tools/generate-link-object
+/install_helpers/tools/test-congestionmark-consumer
+/install_helpers/tools/test-congestionmark-producer
 /install_helpers/tools/test-nack-consumer
 /install_helpers/tools/test-nexthopfaceid-consumer
 /nfd-integ.box
diff --git a/install_helpers/install_NFD.py b/install_helpers/install_NFD.py
index 748af57..ff4db7b 100644
--- a/install_helpers/install_NFD.py
+++ b/install_helpers/install_NFD.py
@@ -8,7 +8,7 @@
     os.system("git clone --depth 1 https://github.com/named-data/NFD")
     os.chdir("NFD")
     os.system("git submodule init && git submodule update")
-    os.system("./waf configure")
+    os.system("./waf configure --with-other-tests")
     os.system("./waf")
     os.system("sudo ./waf install")
     os.system("sudo cp /usr/local/etc/ndn/nfd.conf.sample /usr/local/etc/ndn/nfd.conf")
diff --git a/install_helpers/tools/Makefile b/install_helpers/tools/Makefile
index 6a41305..dd395bd 100644
--- a/install_helpers/tools/Makefile
+++ b/install_helpers/tools/Makefile
@@ -3,7 +3,8 @@
 LIBS = `pkg-config --libs libndn-cxx`
 DESTDIR ?= /usr/local
 
-PROGRAMS = test-nack-consumer test-nexthopfaceid-consumer generate-link-object
+PROGRAMS = test-congestionmark-consumer test-congestionmark-producer test-nack-consumer \
+	test-nexthopfaceid-consumer generate-link-object
 
 all: $(PROGRAMS)
 
diff --git a/install_helpers/tools/test-congestionmark-consumer.cpp b/install_helpers/tools/test-congestionmark-consumer.cpp
new file mode 100644
index 0000000..fb78312
--- /dev/null
+++ b/install_helpers/tools/test-congestionmark-consumer.cpp
@@ -0,0 +1,61 @@
+/*
+ * Consumer for CongestionMark test (test_congestionmark)
+ *
+ * Sends an Interest for a specified prefix and prints the returned congestion mark (or whether
+ * there was a Nack or Timeout).
+ *
+ * Author: Eric Newberry <enewberry@cs.arizona.edu>
+ *
+ * Based on ndn-cxx example consumer
+ */
+
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/lp/nack.hpp>
+
+using namespace ndn;
+
+class TestCongestionMarkConsumer
+{
+public:
+  void
+  run(Name name)
+  {
+    Interest interest(name);
+
+    m_face.expressInterest(interest,
+                           [] (const Interest& interest, const Data& data) {
+                             std::cout << "CongestionMark=" << data.getCongestionMark() << std::endl;
+                           },
+                           [] (const Interest& interest, const lp::Nack& nack) {
+                             std::cout << "Nack" << std::endl;
+                           },
+                           [] (const Interest& interest) {
+                             std::cout << "Timeout" << std::endl;
+                           });
+    m_face.processEvents();
+  }
+
+private:
+  Face m_face;
+};
+
+int
+main(int argc, char** argv)
+{
+  if (argc != 2) {
+    return 2;
+  }
+
+  try {
+    TestCongestionMarkConsumer consumer;
+    consumer.run(Name(argv[1]));
+  }
+  catch (const std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/install_helpers/tools/test-congestionmark-producer.cpp b/install_helpers/tools/test-congestionmark-producer.cpp
new file mode 100644
index 0000000..c83d15d
--- /dev/null
+++ b/install_helpers/tools/test-congestionmark-producer.cpp
@@ -0,0 +1,76 @@
+/*
+ * Producer for CongestionMark test (test_congestionmark)
+ *
+ * Receives Interests for a specified prefix and returns a Data packet with the same congestion mark
+ * as the corresponding Interest.
+ *
+ * Author: Eric Newberry <enewberry@cs.arizona.edu>
+ *
+ * Based on ndn-cxx example producer
+ */
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/interest-filter.hpp>
+#include <ndn-cxx/lp/tags.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+using namespace ndn;
+
+class TestCongestionMarkProducer
+{
+public:
+  void
+  run(std::string prefix)
+  {
+    m_face.setInterestFilter(prefix,
+                             bind(&TestCongestionMarkProducer::onInterest, this, _1, _2),
+                             RegisterPrefixSuccessCallback(),
+                             bind(&TestCongestionMarkProducer::onRegisterFailed, this, _1, _2));
+    m_face.processEvents();
+  }
+
+private:
+  void
+  onInterest(const InterestFilter& filter, const Interest& i)
+  {
+    static const std::string content = "0123456789";
+    shared_ptr<Data> data = make_shared<Data>();
+    data->setName(i.getName());
+    data->setFreshnessPeriod(time::seconds(10));
+    data->setContent(reinterpret_cast<const uint8_t*>(content.c_str()), content.size());
+    data->setCongestionMark(i.getCongestionMark());
+    m_keyChain.sign(*data);
+    m_face.put(*data);
+  }
+
+  void
+  onRegisterFailed(const Name& prefix, const std::string& reason)
+  {
+    std::cerr << "Failed to register prefix " << prefix << " (reason: " + reason + ")" << std::endl;
+    m_face.shutdown();
+  }
+
+private:
+  Face m_face;
+  KeyChain m_keyChain;
+};
+
+int
+main(int argc, char** argv)
+{
+  if (argc != 2) {
+    return 2;
+  }
+
+  try {
+    TestCongestionMarkProducer producer;
+    producer.run(argv[1]);
+  }
+  catch (const std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/test_congestionmark/README.md b/test_congestionmark/README.md
new file mode 100644
index 0000000..0f54a79
--- /dev/null
+++ b/test_congestionmark/README.md
@@ -0,0 +1,37 @@
+# Test CongestionMark tag and LP field
+
+## Topology
+
+C--A--D
+
+## Steps
+
+### Setup
+
+1.  Start NFD on A, C, and D.
+2.  Set route for /arizona/cs toward A on C and toward D on A. Set route for /ucla/cs toward A on C and toward D on A. Set route for /ucsd/caida toward A on C and toward D on A.
+3.  On A, set the strategy for the /arizona/cs namespace to the CongestionMarkStrategy to introduce a congestion mark (value=1) to traversing Interests under this namespace, overriding any previously-set congestion marks.
+4.  D starts a producer for /arizona/cs. This producer will copy any congestion marks on a received Interest to any Data packets produced as a result.
+5.  D starts a producer for /ucla/cs. This producer will copy any congestion marks on a received Interest to any Data packets produced as a result.
+6.  D starts a producer for /ucsd/caida. This producer will copy any congestion marks on a received Interest to any Data packets produced as a result.
+
+### Test Case 1
+
+7.  Start a consumer on C that sends an Interest for /arizona/cs.
+8.  A will add a congestion mark (value=7) to the traversing Interest to indicate congestion.
+9.  D will propagate the congestion mark on the outgoing Interest, if it exists.
+10. If the consumer on C did not receive a Data satisfying its Interest within the timeout or that did not contain a congestion mark (with value=7), fail the test.
+
+### Test Case 2
+
+11. Start a consumer on C that sends an Interest for /ucla/cs.
+12. If the consumer on C did not receive a Data without a congestion mark satisfying its Interest within the timeout, fail the test.
+
+### Test Case 3
+
+13. On C, set the strategy for the /ucsd/caida namespace to the CongestionMarkStrategy to introduce a congestion mark (value=2) to traversing Interests under this namespace, overriding any previously-set congestion marks.
+14. On A, set the strategy for the /ucsd/caida namespace to the CongestionMarkStrategy to introduce a congestion mark (value=3) to traversing Interests under this namespace, overriding any previously-set congestion marks.
+15. Start a consumer on C that sends an Interest for /arizona/cs.
+16. C will add a congestion mark (value=2) to the traversing Interest to indicate congestion.
+17. A will override the congestion mark (setting value=3) on the traversing Interest.
+18. If the consumer on C did not receive a Data satisfying its Interest within the timeout or that did not contain a congestion mark (with value=3), fail the test.
diff --git a/test_congestionmark/congestionmark-test.sh b/test_congestionmark/congestionmark-test.sh
new file mode 100755
index 0000000..10e3747
--- /dev/null
+++ b/test_congestionmark/congestionmark-test.sh
@@ -0,0 +1,164 @@
+#!/bin/bash
+source ../multi-host.conf
+
+echo "host A IP addresses $IP4_A1 and $IP4_A2"
+echo "host C IP address $IP4_C1"
+echo "host D IP address $IP4_D1"
+
+clean_up() {
+  r=$(ssh $CTRL_C "sudo killall nfd" 2>&1)
+  r=$(ssh $CTRL_D "sudo killall test-congestionmark-producer" 2>&1)
+  r=$(ssh $CTRL_D "sudo killall nfd" 2>&1)
+  r=$(sudo killall nfd 2>&1)
+}
+
+### Setup
+
+# Start NFD on hosts A, C, and D
+workdir=$(pwd)
+echo "starting nfd on host A..."
+mkdir -p $workdir/logs; sudo nfd > $workdir/logs/nfd.log 2>&1 &
+sleep 1
+
+echo "starting nfd on host C..."
+ssh $CTRL_C "mkdir -p $workdir/logs; sudo nfd &> $workdir/logs/nfd.log &"
+sleep 1
+
+echo "starting nfd on host D..."
+ssh $CTRL_D "mkdir -p $workdir/logs; sudo nfd &> $workdir/logs/nfd.log &"
+sleep 1
+
+# Create faces
+ssh $CTRL_C "nfdc face create udp4://$IP4_A1"
+nfdc face create udp4://$IP4_D1
+
+# Set up routes
+ssh $CTRL_C "nfdc route add /arizona/cs udp4://$IP4_A1"
+if [[ $? -ne 0 ]]; then
+  echo "C: Failed to create route for /arizona/cs toward A"
+  clean_up
+  exit 1
+fi
+
+ssh $CTRL_C "nfdc route add /ucla/cs udp4://$IP4_A1"
+if [[ $? -ne 0 ]]; then
+  echo "C: Failed to create route for /ucla/cs toward A"
+  clean_up
+  exit 1
+fi
+
+ssh $CTRL_C "nfdc route add /ucsd/caida udp4://$IP4_A1"
+if [[ $? -ne 0 ]]; then
+  echo "C: Failed to create route for /ucsd/caida toward A"
+  clean_up
+  exit 1
+fi
+
+nfdc route add /arizona/cs udp4://$IP4_D1
+if [[ $? -ne 0 ]]; then
+  echo "A: Failed to create route for /arizona/cs toward D"
+  clean_up
+  exit 1
+fi
+
+nfdc route add /ucla/cs udp4://$IP4_D1
+if [[ $? -ne 0 ]]; then
+  echo "A: Failed to create route for /ucla/cs toward D"
+  clean_up
+  exit 1
+fi
+
+nfdc route add /ucsd/caida udp4://$IP4_D1
+if [[ $? -ne 0 ]]; then
+  echo "A: Failed to create route for /ucsd/caida toward D"
+  clean_up
+  exit 1
+fi
+
+### TestCase 1
+
+# A: Use CongestionMarkStrategy (value=7) strategy for /arizona/cs namespace
+nfdc strategy set /arizona/cs /localhost/nfd/strategy/congestion-mark/%FD%01/7/true
+if [[ $? -ne 0 ]]; then
+  echo "A: Failed to set CongestionMarkStrategy for /arizona/cs prefix"
+  clean_up
+  exit 2
+fi
+
+# D: Start producer for /arizona/cs
+ssh $CTRL_D "test-congestionmark-producer /arizona/cs &>/dev/null &"
+
+# C: Send Interest for /arizona/cs
+output=$(ssh $CTRL_C "test-congestionmark-consumer /arizona/cs")
+status=$?
+if [[ $? -ne 0 ]]; then
+  echo "Consumer for Interest /arizona/cs did not exit with status 0"
+  echo "Actual: $status"
+  clean_up
+  exit 6
+fi
+if [[ $output != "CongestionMark=7" ]]; then
+  echo "Interest /arizona/cs not answered with a Data containing a congestion mark with a value of 7"
+  echo "Actual: $output"
+  clean_up
+  exit 3
+fi
+
+### TestCase 2
+
+# D: Start producer for /ucla/cs
+ssh $CTRL_D "test-congestionmark-producer /ucla/cs &>/dev/null &"
+
+# C: Send Interest for /ucla/cs
+output=$(ssh $CTRL_C "test-congestionmark-consumer /ucla/cs")
+status=$?
+if [[ $? -ne 0 ]]; then
+  echo "Consumer for Interest /ucla/cs did not exit with status 0"
+  echo "Actual: $status"
+  clean_up
+  exit 6
+fi
+if [[ $output != "CongestionMark=0" ]]; then
+  echo "Interest /ucla/cs not answered with a Data without a congestion mark"
+  echo "Actual: $output"
+  clean_up
+  exit 4
+fi
+
+### TestCase 3
+
+# D: Start producer for /ucsd/caida
+ssh $CTRL_D "test-congestionmark-producer /ucsd/caida &>/dev/null &"
+sleep 1
+
+# C: Use CongestionMarkStrategy (value=2) strategy for /ucsd/caida namespace
+ssh $CTRL_C "nfdc strategy set /ucsd/caida /localhost/nfd/strategy/congestion-mark/%FD%01/2/true"
+if [[ $? -ne 0 ]]; then
+  echo "C: Failed to set CongestionMarkStrategy for /ucsd/caida prefix"
+  clean_up
+  exit 2
+fi
+
+# A: Use CongestionMarkStrategy (value=3, override) strategy for /ucsd/caida namespace
+nfdc strategy set /ucsd/caida /localhost/nfd/strategy/congestion-mark/%FD%01/3/false
+if [[ $? -ne 0 ]]; then
+  echo "A: Failed to set CongestionMarkStrategy for /ucsd/caida prefix"
+  clean_up
+  exit 2
+fi
+
+# C: Send Interest for /ucsd/caida
+output=$(ssh $CTRL_C "test-congestionmark-consumer /ucsd/caida")
+status=$?
+if [[ $? -ne 0 ]]; then
+  echo "Consumer for Interest /ucsd/caida did not exit with status 0"
+  echo "Actual: $status"
+  clean_up
+  exit 6
+fi
+if [[ $output != "CongestionMark=3" ]]; then
+  echo "Interest /ucsd/caida not answered with a Data containing a congestion mark with a value of 3"
+  echo "Actual: $output"
+  clean_up
+  exit 5
+fi
diff --git a/test_congestionmark/test_congestionmark.py b/test_congestionmark/test_congestionmark.py
new file mode 100644
index 0000000..e9d5a0a
--- /dev/null
+++ b/test_congestionmark/test_congestionmark.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python2
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2017 The University of Arizona
+# Author: Eric Newberry <enewberry@cs.arizona.edu>
+# See COPYING for copyright and distribution information.
+#
+
+import os
+import unittest
+import subprocess
+
+class test_congestionmark(unittest.TestCase):
+    """Test case for CongestionMark field"""
+
+    def setUp(self):
+        print "\nTesting CongestionMark"
+        print "*****************"
+        os.chdir("test_congestionmark")
+
+    def tearDown(self):
+        print "*****************"
+        os.chdir("..")
+
+    def test_congestionmark(self):
+        ret = subprocess.call(["./congestionmark-test.sh"], shell=True)
+        print "Test script return value:", ret
+        errormsg = {
+            1 : "Failed to create required routes",
+            2 : "Failed to set CongestionMarkStrategy",
+            3 : "Interest /arizona/cs timed out, was nacked, or did not contain a congestion mark with a value of 1",
+            4 : "Interest /ucla/cs timed out, was nacked, or contained a congestion mark",
+            5 : "Interest /ucsd/caida timed out, was nacked, or did not contain a congestion mark with a value of 3",
+            6 : "Consumer did not exit with status 0",
+        }
+        if (ret != 0):
+            self.fail(errormsg[ret])