fib: Don't use [midchain] adjacencies to change an interface's feature arc

Type: fix

Using the adjacency to modify the interface's feature arc doesn't work, since there are potentially more than one adj per-interface.
Instead have the interface, when it is created, register what the end node of the feature arc is. This end node is then also used as the interface's tx node (i.e. it is used as the adjacency's next-node).

rename adj-midhcain-tx as 'tunnel-output', that's a bit more intuitive.

There's also a fix in config string handling to:
 1- prevent false sharing of strings when the end node of the arc is different.
 2- call registered listeners when the end node is changed

For IPSec the consequences are that one cannot provide per-adjacency behaviour using different end-nodes - this was previously done for the no-SA and an SA with no protection. These cases are no handled in the esp-encrypt node.

Signed-off-by: Neale Ranns <neale@graphiant.com>
Change-Id: If3a83d03a3000f28820d9a9cb4101d244803d084
diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py
index 6534dd6..763aedd 100644
--- a/test/test_ipsec_tun_if_esp.py
+++ b/test/test_ipsec_tun_if_esp.py
@@ -2668,6 +2668,7 @@
                            self.pg0.remote_ip4)
         self.config_protect(p)
 
+        self.logger.error(self.vapi.cli("sh ipsec sa"))
         self.verify_tun_44(p, count=n_pkts)
 
         # teardown
@@ -3077,6 +3078,15 @@
         self.pg0.generate_remote_hosts(N_NHS)
         self.pg0.configure_ipv4_neighbors()
 
+        r_all = AclRule(True,
+                        src_prefix="0.0.0.0/0",
+                        dst_prefix="0.0.0.0/0",
+                        proto=0)
+        a = VppAcl(self, [r_all]).add_vpp_config()
+
+        VppAclInterface(self, self.pg0.sw_if_index, [a]).add_vpp_config()
+        VppAclInterface(self, p.tun_if.sw_if_index, [a]).add_vpp_config()
+
         # setup some SAs for several next-hops on the interface
         self.multi_params = []
 
@@ -3128,9 +3138,10 @@
                               self.pg0.remote_hosts[ii].ip4)
             self.multi_params.append(p)
 
-            VppIpRoute(self, p.remote_tun_if_host, 32,
-                       [VppRoutePath(p.tun_if.remote_hosts[ii].ip4,
-                                     p.tun_if.sw_if_index)]).add_vpp_config()
+            p.via_tun_route = VppIpRoute(
+                self, p.remote_tun_if_host, 32,
+                [VppRoutePath(p.tun_if.remote_hosts[ii].ip4,
+                              p.tun_if.sw_if_index)]).add_vpp_config()
 
             p.tun_dst = self.pg0.remote_hosts[ii].ip4
 
@@ -3145,6 +3156,21 @@
         for p in self.multi_params:
             self.verify_tun_44(p, count=N_PKTS)
 
+        # remove one tunnel protect, the rest should still work
+        self.multi_params[0].tun_protect.remove_vpp_config()
+        self.verify_tun_dropped_44(self.multi_params[0], count=N_PKTS)
+        self.multi_params[0].via_tun_route.remove_vpp_config()
+        self.verify_tun_dropped_44(self.multi_params[0], count=N_PKTS)
+
+        for p in self.multi_params[1:]:
+            self.verify_tun_44(p, count=N_PKTS)
+
+        self.multi_params[0].tun_protect.add_vpp_config()
+        self.multi_params[0].via_tun_route.add_vpp_config()
+
+        for p in self.multi_params:
+            self.verify_tun_44(p, count=N_PKTS)
+
 
 class TestIpsecItf6MPLS(TemplateIpsec,
                         TemplateIpsecItf6,