VPP-470: Introduce VxLAN-GPE as transport for iOAM.
[vpp.git] / plugins / ioam-plugin / ioam / lib-vxlan-gpe / vxlan_gpe_ioam.c
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c
new file mode 100644 (file)
index 0000000..066f582
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vnet/vxlan-gpe/vxlan_gpe.h>
+#include <vnet/vxlan-gpe/vxlan_gpe_packet.h>
+#include <vnet/ip/format.h>
+#include <ioam/lib-vxlan-gpe/vxlan_gpe_ioam.h>
+
+vxlan_gpe_ioam_main_t vxlan_gpe_ioam_main;
+
+int
+vxlan_gpe_ioam_set_rewrite (vxlan_gpe_tunnel_t * t, int has_trace_option,
+                           int has_pot_option, int has_ppc_option,
+                           u8 ipv6_set)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  u32 size;
+  vxlan_gpe_ioam_hdr_t *vxlan_gpe_ioam_hdr;
+  u8 *current;
+  u8 trace_data_size = 0;
+  u8 pot_data_size = 0;
+
+  if (has_trace_option == 0 && has_pot_option == 0)
+    return -1;
+
+  /* Work out how much space we need */
+  size = sizeof (vxlan_gpe_ioam_hdr_t);
+
+  if (has_trace_option
+      && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] != 0)
+    {
+      size += sizeof (vxlan_gpe_ioam_option_t);
+      size += hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE];
+    }
+  if (has_pot_option
+      && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
+    {
+      size += sizeof (vxlan_gpe_ioam_option_t);
+      size += hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
+    }
+
+  t->rewrite_size = size;
+
+  if (!ipv6_set)
+    {
+      vxlan4_gpe_rewrite (t, size, VXLAN_GPE_PROTOCOL_IOAM,
+                         hm->encap_v4_next_node);
+      vxlan_gpe_ioam_hdr =
+       (vxlan_gpe_ioam_hdr_t *) (t->rewrite +
+                                 sizeof (ip4_vxlan_gpe_header_t));
+    }
+  else
+    {
+      vxlan6_gpe_rewrite (t, size, VXLAN_GPE_PROTOCOL_IOAM,
+                         VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP);
+      vxlan_gpe_ioam_hdr =
+       (vxlan_gpe_ioam_hdr_t *) (t->rewrite +
+                                 sizeof (ip6_vxlan_gpe_header_t));
+    }
+
+
+  vxlan_gpe_ioam_hdr->type = VXLAN_GPE_PROTOCOL_IOAM;
+  /* Length of the header in octets */
+  vxlan_gpe_ioam_hdr->length = size;
+  vxlan_gpe_ioam_hdr->protocol = t->protocol;
+  current = (u8 *) vxlan_gpe_ioam_hdr + sizeof (vxlan_gpe_ioam_hdr_t);
+
+  if (has_trace_option
+      && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] != 0)
+    {
+      if (0 != hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] (current,
+                                                                 &trace_data_size))
+       return -1;
+      current += trace_data_size;
+    }
+  if (has_pot_option
+      && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
+    {
+      pot_data_size =
+       hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
+      if (0 ==
+         hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT]
+         (current, &pot_data_size))
+       current += pot_data_size;
+    }
+
+  return 0;
+}
+
+int
+vxlan_gpe_ioam_clear_rewrite (vxlan_gpe_tunnel_t * t, int has_trace_option,
+                             int has_pot_option, int has_ppc_option,
+                             u8 ipv6_set)
+{
+
+  t->rewrite_size = 0;
+
+  if (!ipv6_set)
+    {
+      vxlan4_gpe_rewrite (t, 0, 0, VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP);
+    }
+  else
+    {
+      vxlan6_gpe_rewrite (t, 0, 0, VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP);
+    }
+
+
+  return 0;
+}
+
+clib_error_t *
+vxlan_gpe_ioam_clear (vxlan_gpe_tunnel_t * t,
+                     int has_trace_option, int has_pot_option,
+                     int has_ppc_option, u8 ipv6_set)
+{
+  int rv;
+  rv = vxlan_gpe_ioam_clear_rewrite (t, 0, 0, 0, 0);
+
+  if (rv == 0)
+    {
+      return (0);
+    }
+  else
+    {
+      return clib_error_return_code (0, rv, 0,
+                                    "vxlan_gpe_ioam_clear_rewrite returned %d",
+                                    rv);
+    }
+
+}
+
+
+clib_error_t *
+vxlan_gpe_ioam_set (vxlan_gpe_tunnel_t * t,
+                   int has_trace_option, int has_pot_option,
+                   int has_ppc_option, u8 ipv6_set)
+{
+  int rv;
+  rv = vxlan_gpe_ioam_set_rewrite (t, has_trace_option,
+                                  has_pot_option, has_ppc_option, ipv6_set);
+
+  if (rv == 0)
+    {
+      return (0);
+    }
+  else
+    {
+      return clib_error_return_code (0, rv, 0,
+                                    "vxlan_gpe_ioam_set_rewrite returned %d",
+                                    rv);
+    }
+
+}
+
+
+static clib_error_t *
+vxlan_gpe_set_ioam_rewrite_command_fn (vlib_main_t * vm,
+                                      unformat_input_t * input,
+                                      vlib_cli_command_t * cmd)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  ip46_address_t local, remote;
+  u8 local_set = 0;
+  u8 remote_set = 0;
+  u8 ipv4_set = 0;
+  u8 ipv6_set = 0;
+  u32 vni;
+  u8 vni_set = 0;
+  u8 disable = 0;
+  clib_error_t *rv = 0;
+  vxlan4_gpe_tunnel_key_t key4;
+  vxlan6_gpe_tunnel_key_t key6;
+  uword *p;
+  vxlan_gpe_main_t *gm = &vxlan_gpe_main;
+  vxlan_gpe_tunnel_t *t = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "local %U", unformat_ip4_address, &local.ip4))
+       {
+         local_set = 1;
+         ipv4_set = 1;
+       }
+      else if (unformat (input, "remote %U",
+                        unformat_ip4_address, &remote.ip4))
+       {
+         remote_set = 1;
+         ipv4_set = 1;
+       }
+      else if (unformat (input, "local %U", unformat_ip6_address, &local.ip6))
+       {
+         local_set = 1;
+         ipv6_set = 1;
+       }
+      else if (unformat (input, "remote %U",
+                        unformat_ip6_address, &remote.ip6))
+       {
+         remote_set = 1;
+         ipv6_set = 1;
+       }
+      else if (unformat (input, "vni %d", &vni))
+       vni_set = 1;
+      else if (unformat (input, "disable"))
+       disable = 1;
+      else
+       break;
+    }
+
+  if (local_set == 0)
+    return clib_error_return (0, "tunnel local address not specified");
+
+  if (remote_set == 0)
+    return clib_error_return (0, "tunnel remote address not specified");
+
+  if (ipv4_set && ipv6_set)
+    return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
+
+  if ((ipv4_set && memcmp (&local.ip4, &remote.ip4, sizeof (local.ip4)) == 0)
+      || (ipv6_set
+         && memcmp (&local.ip6, &remote.ip6, sizeof (local.ip6)) == 0))
+    return clib_error_return (0, "src and dst addresses are identical");
+
+  if (vni_set == 0)
+    return clib_error_return (0, "vni not specified");
+
+
+  if (!ipv6_set)
+    {
+      key4.local = local.ip4.as_u32;
+      key4.remote = remote.ip4.as_u32;
+      key4.vni = clib_host_to_net_u32 (vni << 8);
+      key4.pad = 0;
+
+      p = hash_get_mem (gm->vxlan4_gpe_tunnel_by_key, &key4);
+    }
+  else
+    {
+      key6.local.as_u64[0] = local.ip6.as_u64[0];
+      key6.local.as_u64[1] = local.ip6.as_u64[1];
+      key6.remote.as_u64[0] = remote.ip6.as_u64[0];
+      key6.remote.as_u64[1] = remote.ip6.as_u64[1];
+      key6.vni = clib_host_to_net_u32 (vni << 8);
+
+      p = hash_get_mem (gm->vxlan6_gpe_tunnel_by_key, &key6);
+    }
+
+  if (!p)
+    return clib_error_return (0, "VxLAN Tunnel not found");
+
+  t = pool_elt_at_index (gm->tunnels, p[0]);
+
+  if (!disable)
+    {
+      rv = vxlan_gpe_ioam_set (t, hm->has_trace_option,
+                              hm->has_pot_option,
+                              hm->has_ppc_option, ipv6_set);
+    }
+  else
+    {
+      rv = vxlan_gpe_ioam_clear (t, 0, 0, 0, 0);
+    }
+  return rv;
+}
+
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (vxlan_gpe_set_ioam_rewrite_cmd, static) = {
+  .path = "set vxlan-gpe-ioam",
+  .short_help = "set vxlan-gpe-ioam vxlan <src-ip> <dst_ip> <vnid> [disable]",
+  .function = vxlan_gpe_set_ioam_rewrite_command_fn,
+};
+/* *INDENT-ON* */
+
+
+
+clib_error_t *
+vxlan_gpe_ioam_enable (int has_trace_option, int has_pot_option,
+                      int has_ppc_option)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+
+  hm->has_trace_option = has_trace_option;
+  hm->has_pot_option = has_pot_option;
+  hm->has_ppc_option = has_ppc_option;
+  if (hm->has_trace_option)
+    {
+      vxlan_gpe_trace_profile_setup ();
+    }
+
+  return 0;
+}
+
+clib_error_t *
+vxlan_gpe_ioam_disable (int has_trace_option, int has_pot_option,
+                       int has_ppc_option)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+
+  hm->has_trace_option = has_trace_option;
+  hm->has_pot_option = has_pot_option;
+  hm->has_ppc_option = has_ppc_option;
+  if (!hm->has_trace_option)
+    {
+      vxlan_gpe_trace_profile_cleanup ();
+    }
+
+  return 0;
+}
+
+void
+vxlan_gpe_set_next_override (uword next)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  hm->decap_v4_next_override = next;
+  return;
+}
+
+static clib_error_t *
+vxlan_gpe_set_ioam_flags_command_fn (vlib_main_t * vm,
+                                    unformat_input_t * input,
+                                    vlib_cli_command_t * cmd)
+{
+  int has_trace_option = 0;
+  int has_pot_option = 0;
+  int has_ppc_option = 0;
+  clib_error_t *rv = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "trace"))
+       has_trace_option = 1;
+      else if (unformat (input, "pot"))
+       has_pot_option = 1;
+      else if (unformat (input, "ppc encap"))
+       has_ppc_option = PPC_ENCAP;
+      else if (unformat (input, "ppc decap"))
+       has_ppc_option = PPC_DECAP;
+      else if (unformat (input, "ppc none"))
+       has_ppc_option = PPC_NONE;
+      else
+       break;
+    }
+
+
+  rv =
+    vxlan_gpe_ioam_enable (has_trace_option, has_pot_option, has_ppc_option);
+
+  return rv;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (vxlan_gpe_set_ioam_flags_cmd, static) =
+{
+.path = "set vxlan-gpe-ioam rewrite",
+.short_help = "set vxlan-gpe-ioam [trace] [pot] [ppc <encap|decap>]",
+.function = vxlan_gpe_set_ioam_flags_command_fn,};
+/* *INDENT-ON* */
+
+
+
+
+clib_error_t *
+clear_vxlan_gpe_ioam_rewrite_command_fn (vlib_main_t * vm,
+                                        unformat_input_t * input,
+                                        vlib_cli_command_t * cmd)
+{
+  return (vxlan_gpe_ioam_disable (0, 0, 0));
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (vxlan_gpe_clear_ioam_flags_cmd, static) =
+{
+.path = "clear vxlan-gpe-ioam rewrite",
+.short_help = "clear vxlan-gpe-ioam rewrite",
+.function = clear_vxlan_gpe_ioam_rewrite_command_fn,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */