interface: Allow VLAN tag-rewrite on non-sub-interfaces too.

This fix was first made in
    commit fdea5c6a00b74971dbb1b7ec4e25839a871006ca
but was subsequently lost in
    commit 053204ab039d34a990ff0e14c32ce3b294fcce0e

Added unit test for setting VTR on a non-sub-interface to
help ensure no future regressions of this ability.

Type: fix
Change-Id: I71ce2684fb72383741455829ae2d397ea2e95eae
Signed-off-by: Jon Loeliger <jdl@netgate.com>
diff --git a/src/vnet/interface.api b/src/vnet/interface.api
index efc5bb4..39bb9ca 100644
--- a/src/vnet/interface.api
+++ b/src/vnet/interface.api
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-option version = "3.2.1";
+option version = "3.2.2";
 
 import "vnet/interface_types.api";
 import "vnet/ethernet/ethernet_types.api";
diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c
index 3b0c515..0dad168 100644
--- a/src/vnet/interface_api.c
+++ b/src/vnet/interface_api.c
@@ -272,26 +272,26 @@
       mp->sub_inner_vlan_id = ntohs (sub->eth.inner_vlan_id);
       mp->sub_if_flags =
 	ntohl (sub->eth.raw_flags & SUB_IF_API_FLAG_MASK_VNET);
+    }
 
-      /* vlan tag rewrite data */
-      u32 vtr_op = L2_VTR_DISABLED;
-      u32 vtr_push_dot1q = 0, vtr_tag1 = 0, vtr_tag2 = 0;
+  /* vlan tag rewrite data */
+  u32 vtr_op = L2_VTR_DISABLED;
+  u32 vtr_push_dot1q = 0, vtr_tag1 = 0, vtr_tag2 = 0;
 
-      if (l2vtr_get (am->vlib_main, am->vnet_main, swif->sw_if_index,
-		     &vtr_op, &vtr_push_dot1q, &vtr_tag1, &vtr_tag2) != 0)
-	{
-	  // error - default to disabled
-	  mp->vtr_op = ntohl (L2_VTR_DISABLED);
-	  clib_warning ("cannot get vlan tag rewrite for sw_if_index %d",
-			swif->sw_if_index);
-	}
-      else
-	{
-	  mp->vtr_op = ntohl (vtr_op);
-	  mp->vtr_push_dot1q = ntohl (vtr_push_dot1q);
-	  mp->vtr_tag1 = ntohl (vtr_tag1);
-	  mp->vtr_tag2 = ntohl (vtr_tag2);
-	}
+  if (l2vtr_get (am->vlib_main, am->vnet_main, swif->sw_if_index,
+		 &vtr_op, &vtr_push_dot1q, &vtr_tag1, &vtr_tag2) != 0)
+    {
+      // error - default to disabled
+      mp->vtr_op = ntohl (L2_VTR_DISABLED);
+      clib_warning ("cannot get vlan tag rewrite for sw_if_index %d",
+		    swif->sw_if_index);
+    }
+  else
+    {
+      mp->vtr_op = ntohl (vtr_op);
+      mp->vtr_push_dot1q = ntohl (vtr_push_dot1q);
+      mp->vtr_tag1 = ntohl (vtr_tag1);
+      mp->vtr_tag2 = ntohl (vtr_tag2);
     }
 
   /* pbb tag rewrite data */
diff --git a/src/vnet/interface_cli.c b/src/vnet/interface_cli.c
index a66e157..9de674a 100644
--- a/src/vnet/interface_cli.c
+++ b/src/vnet/interface_cli.c
@@ -277,6 +277,7 @@
   u8 show_addresses = 0;
   u8 show_features = 0;
   u8 show_tag = 0;
+  u8 show_vtr = 0;
   int verbose = 0;
 
   /*
@@ -300,6 +301,8 @@
 	    show_features = 1;
 	  else if (unformat (linput, "tag"))
 	    show_tag = 1;
+	  else if (unformat (linput, "vtr"))
+	    show_vtr = 1;
 	  else if (unformat (linput, "verbose"))
 	    verbose = 1;
 	  else
@@ -312,7 +315,7 @@
 	}
       unformat_free (linput);
     }
-  if (show_features || show_tag)
+  if (show_features || show_tag || show_vtr)
     {
       if (sw_if_index == ~(u32) 0)
 	{
@@ -353,6 +356,27 @@
       return 0;
     }
 
+  /*
+   * Show vlan tag rewrite data for one interface.
+   */
+  if (show_vtr)
+    {
+      u32 vtr_op = L2_VTR_DISABLED;
+      u32 push_dot1q = 0, tag1 = 0, tag2 = 0;
+
+      if (l2vtr_get (vm, vnm, sw_if_index,
+		     &vtr_op, &push_dot1q, &tag1, &tag2) != 0)
+	{
+	  vlib_cli_output (vm, "%U: Problem getting vlan tag-rewrite data",
+			   format_vnet_sw_if_index_name, vnm, sw_if_index);
+	  return 0;
+	}
+      vlib_cli_output (vm, "%U:  VTR %0U",
+		       format_vnet_sw_if_index_name, vnm, sw_if_index,
+		       format_vtr, vtr_op, push_dot1q, tag1, tag2);
+      return 0;
+    }
+
   if (!show_addresses)
     vlib_cli_output (vm, "%U\n", format_vnet_sw_interface, vnm, 0);
 
@@ -474,7 +498,7 @@
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (show_sw_interfaces_command, static) = {
   .path = "show interface",
-  .short_help = "show interface [address|addr|features|feat] [<interface> [<interface> [..]]] [verbose]",
+  .short_help = "show interface [address|addr|features|feat|vtr] [<interface> [<interface> [..]]] [verbose]",
   .function = show_sw_interfaces,
   .is_mp_safe = 1,
 };
diff --git a/src/vnet/interface_format.c b/src/vnet/interface_format.c
index 69f481c..507e267 100644
--- a/src/vnet/interface_format.c
+++ b/src/vnet/interface_format.c
@@ -41,6 +41,42 @@
 #include <vppinfra/bitmap.h>
 #include <vnet/l2/l2_input.h>
 #include <vnet/l2/l2_output.h>
+#include <vnet/l2/l2_vtr.h>
+
+u8 *
+format_vtr (u8 * s, va_list * args)
+{
+  u32 vtr_op = va_arg (*args, u32);
+  u32 dot1q = va_arg (*args, u32);
+  u32 tag1 = va_arg (*args, u32);
+  u32 tag2 = va_arg (*args, u32);
+  switch (vtr_op)
+    {
+    case L2_VTR_DISABLED:
+      return format (s, "none");
+    case L2_VTR_PUSH_1:
+      return format (s, "push-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
+    case L2_VTR_PUSH_2:
+      return format (s, "push-2 %s %d %d", dot1q ? "dot1q" : "dot1ad", tag1,
+		     tag2);
+    case L2_VTR_POP_1:
+      return format (s, "pop-1");
+    case L2_VTR_POP_2:
+      return format (s, "pop-2");
+    case L2_VTR_TRANSLATE_1_1:
+      return format (s, "trans-1-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
+    case L2_VTR_TRANSLATE_1_2:
+      return format (s, "trans-1-2 %s %d %d", dot1q ? "dot1q" : "dot1ad",
+		     tag1, tag2);
+    case L2_VTR_TRANSLATE_2_1:
+      return format (s, "trans-2-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
+    case L2_VTR_TRANSLATE_2_2:
+      return format (s, "trans-2-2 %s %d %d", dot1q ? "dot1q" : "dot1ad",
+		     tag1, tag2);
+    default:
+      return format (s, "none");
+    }
+}
 
 u8 *
 format_vnet_sw_interface_flags (u8 * s, va_list * args)
diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h
index 9241b34..388a438 100644
--- a/src/vnet/interface_funcs.h
+++ b/src/vnet/interface_funcs.h
@@ -448,6 +448,9 @@
 unformat_function_t unformat_vnet_hw_interface_flags;
 unformat_function_t unformat_vnet_sw_interface_flags;
 
+/* VLAN tag-rewrite */
+format_function_t format_vtr;
+
 /* Node runtime for interface output function. */
 typedef struct
 {
diff --git a/src/vnet/l2/l2_bd.c b/src/vnet/l2/l2_bd.c
index 207ef4d..e54d437 100644
--- a/src/vnet/l2/l2_bd.c
+++ b/src/vnet/l2/l2_bd.c
@@ -995,41 +995,6 @@
 /* *INDENT-ON* */
 
 static u8 *
-format_vtr (u8 * s, va_list * args)
-{
-  u32 vtr_op = va_arg (*args, u32);
-  u32 dot1q = va_arg (*args, u32);
-  u32 tag1 = va_arg (*args, u32);
-  u32 tag2 = va_arg (*args, u32);
-  switch (vtr_op)
-    {
-    case L2_VTR_DISABLED:
-      return format (s, "none");
-    case L2_VTR_PUSH_1:
-      return format (s, "push-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
-    case L2_VTR_PUSH_2:
-      return format (s, "push-2 %s %d %d", dot1q ? "dot1q" : "dot1ad", tag1,
-		     tag2);
-    case L2_VTR_POP_1:
-      return format (s, "pop-1");
-    case L2_VTR_POP_2:
-      return format (s, "pop-2");
-    case L2_VTR_TRANSLATE_1_1:
-      return format (s, "trans-1-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
-    case L2_VTR_TRANSLATE_1_2:
-      return format (s, "trans-1-2 %s %d %d", dot1q ? "dot1q" : "dot1ad",
-		     tag1, tag2);
-    case L2_VTR_TRANSLATE_2_1:
-      return format (s, "trans-2-1 %s %d", dot1q ? "dot1q" : "dot1ad", tag1);
-    case L2_VTR_TRANSLATE_2_2:
-      return format (s, "trans-2-2 %s %d %d", dot1q ? "dot1q" : "dot1ad",
-		     tag1, tag2);
-    default:
-      return format (s, "none");
-    }
-}
-
-static u8 *
 format_uu_cfg (u8 * s, va_list * args)
 {
   l2_bridge_domain_t *bd_config = va_arg (*args, l2_bridge_domain_t *);
diff --git a/test/test_vtr.py b/test/test_vtr.py
index 239767a..c3704f1 100644
--- a/test/test_vtr.py
+++ b/test/test_vtr.py
@@ -331,6 +331,56 @@
         self.vtr_test(self.pg2, [Tag(dot1=DOT1AD, vlan=400),
                                  Tag(dot1=DOT1Q, vlan=300)])
 
+    def test_if_vtr_disable(self):
+        """ Disable VTR on non-sub-interfaces
+        """
+        # First set the VTR fields to junk
+        self.vapi.l2_interface_vlan_tag_rewrite(
+            sw_if_index=self.pg0.sw_if_index, vtr_op=L2_VTR_OP.L2_PUSH_2,
+            push_dot1q=1, tag1=19, tag2=630)
+
+        if_state = self.vapi.sw_interface_dump(
+            sw_if_index=self.pg0.sw_if_index)
+        self.assertEqual(if_state[0].sw_if_index, self.pg0.sw_if_index)
+        self.assertNotEqual(if_state[0].vtr_op, L2_VTR_OP.L2_DISABLED)
+
+        # Then ensure that a request to disable VTR is honored.
+        self.vapi.l2_interface_vlan_tag_rewrite(
+            sw_if_index=self.pg0.sw_if_index, vtr_op=L2_VTR_OP.L2_DISABLED)
+
+        if_state = self.vapi.sw_interface_dump(
+            sw_if_index=self.pg0.sw_if_index)
+        self.assertEqual(if_state[0].sw_if_index, self.pg0.sw_if_index)
+        self.assertEqual(if_state[0].vtr_op, L2_VTR_OP.L2_DISABLED)
+
+    def test_if_vtr_push_1q(self):
+        """ 1Q VTR push 1 on non-sub-interfaces
+        """
+        self.vapi.l2_interface_vlan_tag_rewrite(
+            sw_if_index=self.pg0.sw_if_index, vtr_op=L2_VTR_OP.L2_PUSH_1,
+            push_dot1q=1, tag1=150)
+
+        if_state = self.vapi.sw_interface_dump(
+            sw_if_index=self.pg0.sw_if_index)
+        self.assertEqual(if_state[0].sw_if_index, self.pg0.sw_if_index)
+        self.assertEqual(if_state[0].vtr_op, L2_VTR_OP.L2_PUSH_1)
+        self.assertEqual(if_state[0].vtr_tag1, 150)
+        self.assertNotEqual(if_state[0].vtr_push_dot1q, 0)
+
+    def test_if_vtr_push_2ad(self):
+        """ 1AD VTR push 2 on non-sub-interfaces
+        """
+        self.vapi.l2_interface_vlan_tag_rewrite(
+            sw_if_index=self.pg0.sw_if_index, vtr_op=L2_VTR_OP.L2_PUSH_2,
+            push_dot1q=0, tag1=450, tag2=350)
+
+        if_state = self.vapi.sw_interface_dump(
+            sw_if_index=self.pg0.sw_if_index)
+        self.assertEqual(if_state[0].sw_if_index, self.pg0.sw_if_index)
+        self.assertEqual(if_state[0].vtr_op, L2_VTR_OP.L2_PUSH_2)
+        self.assertEqual(if_state[0].vtr_tag1, 450)         # outer
+        self.assertEqual(if_state[0].vtr_tag2, 350)         # inner
+        self.assertEqual(if_state[0].vtr_push_dot1q, 0)
 
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)