Link failure test scenario
refs #1661
Change-Id: I0cc2e66ea6fb1ec1ebf4517fd4cd4072fa9f231e
diff --git a/multi-host.conf b/multi-host.conf
index 469be7d..0991872 100644
--- a/multi-host.conf
+++ b/multi-host.conf
@@ -11,40 +11,22 @@
# IPv4: /24
# IPv6: /64
#
-# Sample config:
-# CTRL_B=10.0.0.1
-# CTRL_C=10.0.0.3
-# CTRL_D=10.0.0.18
-# CTRL_R=10.0.0.10
-# IP4_A1=10.0.0.2
-# IP4_B1=10.0.0.1
-# IP4_C1=10.0.0.3
-# IP4_A2=10.1.0.1
-# IP4_R1=10.1.0.2
-# IP4_R2=10.2.0.1
-# IP4_D1=10.2.0.2
-# IP6_A1=fd8c:edca:0f3c:10f4::2
-# IP6_B1=fd8c:edca:0f3c:10f4::1
-# IP6_C1=fd8c:edca:0f3c:10f4::3
-# IP6_A2=fd8c:edca:0f3c:10f5::9
-# IP4_R1=fd8c:edca:0f3c:10f5::a
-# IP4_R2=fd8c:edca:0f3c:10f6::11
-# IP6_D1=fd8c:edca:0f3c:10f6::12
-CTRL_B=10.0.0.1
-CTRL_C=10.0.0.3
-CTRL_D=10.0.0.18
-CTRL_R=10.0.0.10
+CTRL_B=b.integ.ndn-routing.emulab.net
+CTRL_C=c.integ.ndn-routing.emulab.net
+CTRL_D=d.integ.ndn-routing.emulab.net
+CTRL_R=r.integ.ndn-routing.emulab.net
IP4_A1=10.0.0.2
IP4_B1=10.0.0.1
IP4_C1=10.0.0.3
-IP4_A2=10.0.0.9
-IP4_R1=10.0.0.10
-IP4_R2=10.0.0.25
-IP4_D1=10.0.0.26
+IP4_A2=10.1.0.1
+IP4_R1=10.1.0.2
+IP4_R2=10.2.0.1
+IP4_D1=10.2.0.2
IP6_A1=fd8c:edca:0f3c:10f4::2
IP6_B1=fd8c:edca:0f3c:10f4::1
IP6_C1=fd8c:edca:0f3c:10f4::3
-IP6_A2=fd8c:edca:0f3c:10f4::9
-IP4_R1=fd8c:edca:0f3c:10f4::a
-IP4_R2=fd8c:edca:0f3c:10f4::11
-IP6_D1=fd8c:edca:0f3c:10f4::12
+IP6_A2=fd8c:edca:0f3c:10f5::9
+IP6_R1=fd8c:edca:0f3c:10f5::a
+IP6_R2=fd8c:edca:0f3c:10f6::11
+IP6_D1=fd8c:edca:0f3c:10f6::12
+
diff --git a/test_linkfail/README.md b/test_linkfail/README.md
new file mode 100644
index 0000000..2e5da51
--- /dev/null
+++ b/test_linkfail/README.md
@@ -0,0 +1,30 @@
+# Link Failure test scenario
+
+This scenario tests whether a forwarding strategy can react to link failures.
+
+Three forwarding strategies are tested:
+
+* NCC strategy
+* Broadcast strategy
+* Best Route strategy: expected failure
+
+## Topology
+
+ /-----\
+ A B
+ \--C--/
+
+## Steps
+
+1. start NFD, set strategy for root namespace
+2. setup RIB entries on hosts: C has a route toward B (cost=10), A has a route toward B (cost=10) and a route toward C (cost=20)
+3. run ping server on B, run ping client on A
+4. after 5s: fail link A-B
+5. after 5s: recover link A-B
+6. after 5s: fail link A-C
+7. after 5s: recover link A-C
+8. after 5s: stop ping client
+9. analyze ping client logs, fail the test case if there is any loss, or RTT is over 100ms for any sequence number
+
+To inject a link failure, this test scenario invokes `iptables` to drop UDP packets on the port number used by the tunnel.
+We don't destroy the face via `nfdc`, because that would cause the nexthop record to be removed from FIB entries, which can be detected by the strategy, and the reaction would be different from a temporary link failure.
diff --git a/test_linkfail/__init__.py b/test_linkfail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test_linkfail/__init__.py
diff --git a/test_linkfail/linkfail.sh b/test_linkfail/linkfail.sh
new file mode 100755
index 0000000..63e834f
--- /dev/null
+++ b/test_linkfail/linkfail.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source ../multi-host.conf
+
+# linkfail.sh fail|recover end1 end2
+
+ACT=$1
+END1=$2
+END2=$3
+CTRLVAR1='CTRL_'${END1:0:1}
+CTRLVAR2='CTRL_'${END2:0:1}
+IP4VAR1='IP4_'$END1
+IP4VAR2='IP4_'$END2
+
+CMD='-D'
+if [[ $ACT == 'fail' ]]
+then
+ CMD='-I'
+fi
+
+ssh ${!CTRLVAR1} "sudo iptables $CMD INPUT -s ${!IP4VAR2}/32 -p udp --dport 6363 -j DROP"
+ssh ${!CTRLVAR2} "sudo iptables $CMD INPUT -s ${!IP4VAR1}/32 -p udp --dport 6363 -j DROP"
+
diff --git a/test_linkfail/ping.sh b/test_linkfail/ping.sh
new file mode 100755
index 0000000..6b741eb
--- /dev/null
+++ b/test_linkfail/ping.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source ../multi-host.conf
+
+LOGPREFIX=$1
+
+ndnping -c 25 /B &> logs/${LOGPREFIX}_ping_A.log
+
+LOSS=$(sed -n 's/.*Packet Loss=\([^,]*\).*/\1/p' logs/${LOGPREFIX}_ping_A.log)
+MAXRTT=$(sed -n 's/Round Trip Time.* = (\([^)]*\).*/\1/p' logs/${LOGPREFIX}_ping_A.log | cut -d/ -f2)
+
+[[ $LOSS == '0%' ]] && [[ $(echo $MAXRTT | sed 's/\..*//') -le 100 ]]
+
diff --git a/test_linkfail/pingserver.sh b/test_linkfail/pingserver.sh
new file mode 100755
index 0000000..2ce10cd
--- /dev/null
+++ b/test_linkfail/pingserver.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source ../multi-host.conf
+
+LOGPREFIX=$1
+
+ssh $CTRL_B "cd $DIR; ndnpingserver /B &> logs/${LOGPREFIX}_pingserver_B.log"
+
diff --git a/test_linkfail/start.sh b/test_linkfail/start.sh
new file mode 100755
index 0000000..6f59c15
--- /dev/null
+++ b/test_linkfail/start.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source ../multi-host.conf
+
+LOGPREFIX=$1
+STRATEGY=$2
+
+mkdir -p logs; nfd-start &> logs/${LOGPREFIX}_nfd_A.log
+ssh $CTRL_B "cd $DIR; mkdir -p logs; nfd-start &> logs/${LOGPREFIX}_nfd_B.log"
+ssh $CTRL_C "cd $DIR; mkdir -p logs; nfd-start &> logs/${LOGPREFIX}_nfd_C.log"
+
+sleep 1
+> logs/${LOGPREFIX}_nfdc.log
+
+nfdc set-strategy / $STRATEGY &>> logs/${LOGPREFIX}_nfdc.log
+ssh $CTRL_B "nfdc set-strategy / $STRATEGY" &>> logs/${LOGPREFIX}_nfdc.log
+ssh $CTRL_C "nfdc set-strategy / $STRATEGY" &>> logs/${LOGPREFIX}_nfdc.log
+
+ssh $CTRL_C "nfdc register -c 10 /B udp4://$IP4_B1" &>> logs/${LOGPREFIX}_nfdc.log
+nfdc register -c 10 /B udp4://$IP4_B1 &>> logs/${LOGPREFIX}_nfdc.log
+nfdc register -c 20 /B udp4://$IP4_C1 &>> logs/${LOGPREFIX}_nfdc.log
+
diff --git a/test_linkfail/stop.sh b/test_linkfail/stop.sh
new file mode 100755
index 0000000..ae5a316
--- /dev/null
+++ b/test_linkfail/stop.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source ../multi-host.conf
+
+nfd-stop
+ssh $CTRL_B nfd-stop
+ssh $CTRL_C nfd-stop
+
diff --git a/test_linkfail/test_linkfail.py b/test_linkfail/test_linkfail.py
new file mode 100644
index 0000000..2d8afa9
--- /dev/null
+++ b/test_linkfail/test_linkfail.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python2
+
+import os
+import time
+import unittest
+import process_manager
+
+class test_linkfail(unittest.TestCase, process_manager.ProcessManager):
+ """Strategy link failure test scenario"""
+
+ def setUp(self):
+ os.chdir("test_linkfail")
+
+ def tearDown(self):
+ self.startProcess("stop", ["./stop.sh"], "-> Stopping NFD")
+ self.waitForProcessCompletion("stop", None)
+ os.chdir("..")
+
+ def run_strategy_test(self, key, strategy, expectLoss=False):
+ print "[linkfail] testing", key
+ self.startProcess("start", ["./start.sh", key, strategy], "-> Starting NFD and creating faces")
+ self.waitForProcessCompletion("start", None)
+ self.startProcess("pingserver", ["./pingserver.sh", key], "-> Starting ping server on B")
+ time.sleep(1)
+ self.startProcess("ping", ["./ping.sh", key], "-> Starting ping client on A")
+
+ time.sleep(5)
+ self.startProcess("ABfail", ["./linkfail.sh", "fail", "A1", "B1"], "-> Failing link A-B")
+ self.waitForProcessCompletion("ABfail", None)
+ time.sleep(5)
+ self.startProcess("ABrecover", ["./linkfail.sh", "recover", "A1", "B1"], "-> Recovering link A-B")
+ self.waitForProcessCompletion("ABrecover", None)
+ time.sleep(5)
+ self.startProcess("ACfail", ["./linkfail.sh", "fail", "A1", "C1"], "-> Failing link A-C")
+ self.waitForProcessCompletion("ACfail", None)
+ time.sleep(5)
+ self.startProcess("ACrecover", ["./linkfail.sh", "recover", "A1", "C1"], "-> Recovering link A-C")
+ self.waitForProcessCompletion("ACrecover", None)
+ self.waitForProcessCompletion("ping", None)
+ self.killProcess("pingserver")
+
+ if expectLoss:
+ if self.getProcessReturnCode("ping") == 0:
+ self.fail("EXPECTED loss or delay; ACTUAL no loss or delay")
+ else:
+ if self.getProcessReturnCode("ping") <> 0:
+ self.fail("ping loss or delay")
+
+ def test_broadcast(self):
+ self.run_strategy_test("broadcast", "ndn:/localhost/nfd/strategy/broadcast")
+
+ def test_ncc(self):
+ self.run_strategy_test("ncc", "ndn:/localhost/nfd/strategy/ncc")
+
+ def test_bestroute(self):
+ self.run_strategy_test("bestroute", "ndn:/localhost/nfd/strategy/best-route", True)