VPP-202: L2-GRE over IPSec 11/2311/5
authorMatus Fabian <matfabia@cisco.com>
Wed, 10 Aug 2016 08:55:36 +0000 (01:55 -0700)
committerKeith Burns <alagalah@gmail.com>
Wed, 17 Aug 2016 23:21:46 +0000 (23:21 +0000)
GRE encapsulate layer 2 traffic and IPSec encrypt what is encapsulated by GRE.
The whole point of L2-GRE over IPSec is to tunnel layer 2 over GRE and IPSec by
bridging the physical interface with IPSec-GRE tunnel interface.

Change-Id: Ia4cf9ed407bf663770e0d8905c0ad44ce73bd23b
Signed-off-by: Matus Fabian <matfabia@cisco.com>
21 files changed:
vnet/Makefile.am
vnet/vnet/buffer.h
vnet/vnet/ipsec-gre/dir.dox [new file with mode: 0644]
vnet/vnet/ipsec-gre/error.def [new file with mode: 0644]
vnet/vnet/ipsec-gre/interface.c [new file with mode: 0644]
vnet/vnet/ipsec-gre/ipsec_gre.c [new file with mode: 0644]
vnet/vnet/ipsec-gre/ipsec_gre.h [new file with mode: 0644]
vnet/vnet/ipsec-gre/ipsec_gre_doc.md [new file with mode: 0644]
vnet/vnet/ipsec-gre/node.c [new file with mode: 0644]
vnet/vnet/ipsec/esp_decrypt.c
vnet/vnet/ipsec/esp_encrypt.c
vnet/vnet/ipsec/ipsec.c
vnet/vnet/ipsec/ipsec.h
vnet/vnet/ipsec/ipsec_cli.c
vnet/vnet/ipsec/ipsec_if.c
vnet/vnet/ipsec/ipsec_if_in.c
vnet/vnet/ipsec/ipsec_input.c
vpp-api-test/vat/api_format.c
vpp/vpp-api/api.c
vpp/vpp-api/custom_dump.c
vpp/vpp-api/vpe.api

index 472fc71..72bdd11 100644 (file)
@@ -415,6 +415,18 @@ nobase_include_HEADERS +=                  \
  vnet/vxlan-gpe/vxlan_gpe_packet.h             \
  vnet/vxlan-gpe/vxlan_gpe_error.def
 
+########################################
+# Tunnel protocol: ipsec+gre
+########################################
+libvnet_la_SOURCES +=                          \
+ vnet/ipsec-gre/ipsec_gre.c                    \
+ vnet/ipsec-gre/node.c                         \
+ vnet/ipsec-gre/interface.c
+
+nobase_include_HEADERS +=                      \
+ vnet/ipsec-gre/ipsec_gre.h                    \
+ vnet/ipsec-gre/error.def
+
 ########################################
 # LISP control plane: lisp-cp
 ########################################
index 3fcdf07..cab716c 100644 (file)
@@ -212,7 +212,7 @@ typedef struct
     /* interface output features */
     struct
     {
-      u32 ipsec_spd_index;
+      u32 ipsec_flags;
       u32 ipsec_sad_index;
       u32 unused[3];
       u32 bitmap;
diff --git a/vnet/vnet/ipsec-gre/dir.dox b/vnet/vnet/ipsec-gre/dir.dox
new file mode 100644 (file)
index 0000000..e6ffd10
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+/**
+ @dir vnet/vnet/ipsec-gre
+ @brief L2-GRE over IPSec tunnel interface implementation
+*/
diff --git a/vnet/vnet/ipsec-gre/error.def b/vnet/vnet/ipsec-gre/error.def
new file mode 100644 (file)
index 0000000..0d7b468
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+/**
+ * @file error.def
+ * @brief L2-GRE over IPSec errors.
+ */
+
+
+ipsec_gre_error (NONE, "no error")
+ipsec_gre_error (UNKNOWN_PROTOCOL, "unknown protocol")
+ipsec_gre_error (UNSUPPORTED_VERSION, "unsupported version")
+ipsec_gre_error (PKTS_DECAP, "GRE input packets decapsulated")
+ipsec_gre_error (PKTS_ENCAP, "GRE output packets encapsulated")
+ipsec_gre_error (NO_SUCH_TUNNEL, "GRE input packets dropped due to missing tunnel")
diff --git a/vnet/vnet/ipsec-gre/interface.c b/vnet/vnet/ipsec-gre/interface.c
new file mode 100644 (file)
index 0000000..3e5d395
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * gre_interface.c: gre interfaces
+ *
+ * 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.
+ */
+/**
+ * @file interface.c
+ * @brief L2-GRE over IPSec tunnel interface.
+ *
+ * Creates ipsec-gre tunnel interface.
+ * Provides a command line interface so humans can interact with VPP.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vnet/ipsec-gre/ipsec_gre.h>
+#include <vnet/ip/format.h>
+#include <vnet/ipsec/ipsec.h>
+#include <vnet/ipsec/esp.h>
+
+u8 *
+format_ipsec_gre_tunnel (u8 * s, va_list * args)
+{
+  ipsec_gre_tunnel_t *t = va_arg (*args, ipsec_gre_tunnel_t *);
+  ipsec_gre_main_t *gm = &ipsec_gre_main;
+
+  s = format (s,
+             "[%d] %U (src) %U (dst) local-sa %d remote-sa %d",
+             t - gm->tunnels,
+             format_ip4_address, &t->tunnel_src,
+             format_ip4_address, &t->tunnel_dst,
+             t->local_sa_id, t->remote_sa_id);
+  return s;
+}
+
+static clib_error_t *
+show_ipsec_gre_tunnel_command_fn (vlib_main_t * vm,
+                                 unformat_input_t * input,
+                                 vlib_cli_command_t * cmd)
+{
+  ipsec_gre_main_t *igm = &ipsec_gre_main;
+  ipsec_gre_tunnel_t *t;
+
+  if (pool_elts (igm->tunnels) == 0)
+    vlib_cli_output (vm, "No IPSec GRE tunnels configured...");
+
+  /* *INDENT-OFF* */
+  pool_foreach (t, igm->tunnels,
+  ({
+    vlib_cli_output (vm, "%U", format_ipsec_gre_tunnel, t);
+  }));
+  /* *INDENT-ON* */
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_ipsec_gre_tunnel_command, static) = {
+    .path = "show ipsec gre tunnel",
+    .function = show_ipsec_gre_tunnel_command_fn,
+};
+/* *INDENT-ON* */
+
+/* force inclusion from application's main.c */
+clib_error_t *
+ipsec_gre_interface_init (vlib_main_t * vm)
+{
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (ipsec_gre_interface_init);
+
+/**
+ * @brief Add or delete ipsec-gre tunnel interface.
+ *
+ * @param *a vnet_ipsec_gre_add_del_tunnel_args_t - tunnel interface parameters
+ * @param *sw_if_indexp u32 - software interface index
+ * @return int - 0 if success otherwise <code>VNET_API_ERROR_</code>
+ */
+int
+vnet_ipsec_gre_add_del_tunnel (vnet_ipsec_gre_add_del_tunnel_args_t * a,
+                              u32 * sw_if_indexp)
+{
+  ipsec_gre_main_t *igm = &ipsec_gre_main;
+  vnet_main_t *vnm = igm->vnet_main;
+  ip4_main_t *im = &ip4_main;
+  ipsec_gre_tunnel_t *t;
+  vnet_hw_interface_t *hi;
+  u32 hw_if_index, sw_if_index;
+  u32 slot;
+  uword *p;
+  u64 key;
+  ipsec_add_del_ipsec_gre_tunnel_args_t args;
+
+  memset (&args, 0, sizeof (args));
+  args.is_add = a->is_add;
+  args.local_sa_id = a->lsa;
+  args.remote_sa_id = a->rsa;
+  args.local_ip.as_u32 = a->src.as_u32;
+  args.remote_ip.as_u32 = a->dst.as_u32;
+
+  key = (u64) a->src.as_u32 << 32 | (u64) a->dst.as_u32;
+  p = hash_get (igm->tunnel_by_key, key);
+
+  if (a->is_add)
+    {
+      /* check if same src/dst pair exists */
+      if (p)
+       return VNET_API_ERROR_INVALID_VALUE;
+
+      pool_get_aligned (igm->tunnels, t, CLIB_CACHE_LINE_BYTES);
+      memset (t, 0, sizeof (*t));
+
+      if (vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) > 0)
+       {
+         vnet_interface_main_t *im = &vnm->interface_main;
+
+         hw_if_index = igm->free_ipsec_gre_tunnel_hw_if_indices
+           [vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) - 1];
+         _vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) -= 1;
+
+         hi = vnet_get_hw_interface (vnm, hw_if_index);
+         hi->dev_instance = t - igm->tunnels;
+         hi->hw_instance = hi->dev_instance;
+
+         /* clear old stats of freed tunnel before reuse */
+         sw_if_index = hi->sw_if_index;
+         vnet_interface_counter_lock (im);
+         vlib_zero_combined_counter
+           (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX],
+            sw_if_index);
+         vlib_zero_combined_counter
+           (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX],
+            sw_if_index);
+         vlib_zero_simple_counter
+           (&im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP], sw_if_index);
+         vnet_interface_counter_unlock (im);
+       }
+      else
+       {
+         hw_if_index = vnet_register_interface
+           (vnm, ipsec_gre_device_class.index, t - igm->tunnels,
+            ipsec_gre_hw_interface_class.index, t - igm->tunnels);
+         hi = vnet_get_hw_interface (vnm, hw_if_index);
+         sw_if_index = hi->sw_if_index;
+       }
+
+      t->hw_if_index = hw_if_index;
+      t->sw_if_index = sw_if_index;
+      t->local_sa_id = a->lsa;
+      t->remote_sa_id = a->rsa;
+      t->local_sa = ipsec_get_sa_index_by_sa_id (a->lsa);
+      t->remote_sa = ipsec_get_sa_index_by_sa_id (a->rsa);
+
+      vec_validate_init_empty (igm->tunnel_index_by_sw_if_index,
+                              sw_if_index, ~0);
+      igm->tunnel_index_by_sw_if_index[sw_if_index] = t - igm->tunnels;
+
+      vec_validate (im->fib_index_by_sw_if_index, sw_if_index);
+
+      hi->min_packet_bytes = 64 + sizeof (gre_header_t) +
+       sizeof (ip4_header_t) + sizeof (esp_header_t) + sizeof (esp_footer_t);
+      hi->per_packet_overhead_bytes =
+       /* preamble */ 8 + /* inter frame gap */ 12;
+
+      /* Standard default gre MTU. */
+      hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] =
+       9000;
+
+      clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src));
+      clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst));
+
+      hash_set (igm->tunnel_by_key, key, t - igm->tunnels);
+
+      slot = vlib_node_add_named_next_with_slot
+       (vnm->vlib_main, hi->tx_node_index, "esp-encrypt",
+        IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT);
+
+      ASSERT (slot == IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT);
+
+    }
+  else
+    {                          /* !is_add => delete */
+      /* tunnel needs to exist */
+      if (!p)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+      t = pool_elt_at_index (igm->tunnels, p[0]);
+
+      sw_if_index = t->sw_if_index;
+      vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ );
+      /* make sure tunnel is removed from l2 bd or xconnect */
+      set_int_l2_mode (igm->vlib_main, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0);
+      vec_add1 (igm->free_ipsec_gre_tunnel_hw_if_indices, t->hw_if_index);
+      igm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
+
+      hash_unset (igm->tunnel_by_key, key);
+      pool_put (igm->tunnels, t);
+    }
+
+  if (sw_if_indexp)
+    *sw_if_indexp = sw_if_index;
+
+  return ipsec_add_del_ipsec_gre_tunnel (vnm, &args);
+}
+
+static clib_error_t *
+create_ipsec_gre_tunnel_command_fn (vlib_main_t * vm,
+                                   unformat_input_t * input,
+                                   vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u8 is_add = 1;
+  u32 num_m_args = 0;
+  ip4_address_t src, dst;
+  u32 lsa = 0, rsa = 0;
+  vnet_ipsec_gre_add_del_tunnel_args_t _a, *a = &_a;
+  int rv;
+  u32 sw_if_index;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "del"))
+       is_add = 0;
+      else if (unformat (line_input, "src %U", unformat_ip4_address, &src))
+       num_m_args++;
+      else if (unformat (line_input, "dst %U", unformat_ip4_address, &dst))
+       num_m_args++;
+      else if (unformat (line_input, "local-sa %d", &lsa))
+       num_m_args++;
+      else if (unformat (line_input, "remote-sa %d", &rsa))
+       num_m_args++;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+  unformat_free (line_input);
+
+  if (num_m_args < 4)
+    return clib_error_return (0, "mandatory argument(s) missing");
+
+  if (memcmp (&src, &dst, sizeof (src)) == 0)
+    return clib_error_return (0, "src and dst are identical");
+
+  memset (a, 0, sizeof (*a));
+  a->is_add = is_add;
+  a->lsa = lsa;
+  a->rsa = rsa;
+  clib_memcpy (&a->src, &src, sizeof (src));
+  clib_memcpy (&a->dst, &dst, sizeof (dst));
+
+  rv = vnet_ipsec_gre_add_del_tunnel (a, &sw_if_index);
+
+  switch (rv)
+    {
+    case 0:
+      vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
+                      vnet_get_main (), sw_if_index);
+      break;
+    case VNET_API_ERROR_INVALID_VALUE:
+      return clib_error_return (0, "GRE tunnel already exists...");
+    default:
+      return clib_error_return (0,
+                               "vnet_ipsec_gre_add_del_tunnel returned %d",
+                               rv);
+    }
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (create_ipsec_gre_tunnel_command, static) = {
+  .path = "create ipsec gre tunnel",
+  .short_help = "create ipsec gre tunnel src <addr> dst <addr> "
+                "local-sa <id> remote-sa <id> [del]",
+  .function = create_ipsec_gre_tunnel_command_fn,
+};
+/* *INDENT-ON* */
+
+/*
+* fd.io coding-style-patch-verification: ON
+*
+* Local Variables:
+* eval: (c-set-style "gnu")
+* End:
+*/
diff --git a/vnet/vnet/ipsec-gre/ipsec_gre.c b/vnet/vnet/ipsec-gre/ipsec_gre.c
new file mode 100644 (file)
index 0000000..24ec6f4
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * 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.
+ */
+/**
+ * @file ipsec_gre.c
+ * @brief L2-GRE over IPSec packet processing.
+ *
+ * Add GRE header to thr packet and send it to the esp-encrypt node.
+*/
+
+#include <vnet/vnet.h>
+#include <vnet/ipsec-gre/ipsec_gre.h>
+
+ipsec_gre_main_t ipsec_gre_main;
+
+/**
+ * @brief IPv4 and GRE header.
+ *
+*/
+/* *INDENT-OFF* */
+typedef CLIB_PACKED (struct
+{
+  ip4_header_t ip4;
+  gre_header_t gre;
+}) ip4_and_gre_header_t;
+/* *INDENT-OFF* */
+
+/**
+ * @brief IPv4 and GRE header union.
+ *
+*/
+typedef struct
+{
+  union
+  {
+    ip4_and_gre_header_t ip4_and_gre;
+    u64 as_u64[3];
+  };
+} ip4_and_gre_union_t;
+
+/**
+ * @brief Packet trace.
+ *
+*/
+typedef struct
+{
+  u32 tunnel_id; /**< Tunnel-id / index in tunnel vector */
+
+  u32 length; /**< pkt length */
+
+  ip4_address_t src; /**< tunnel src IPv4 address */
+  ip4_address_t dst; /**< tunnel dst IPv4 address */
+
+  u32 sa_id; /**< tunnel IPSec SA id */
+} ipsec_gre_tx_trace_t;
+
+u8 *
+format_ipsec_gre_tx_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  ipsec_gre_tx_trace_t *t = va_arg (*args, ipsec_gre_tx_trace_t *);
+
+  s = format (s, "GRE: tunnel %d len %d src %U dst %U sa-id %d",
+             t->tunnel_id, clib_net_to_host_u16 (t->length),
+             format_ip4_address, &t->src.as_u8,
+             format_ip4_address, &t->dst.as_u8, t->sa_id);
+  return s;
+}
+
+/**
+ * @brief IPSec-GRE tunnel interface tx function.
+ *
+ * Add GRE header to the packet.
+ *
+ * @param vm vlib_main_t corresponding to the current thread.
+ * @param node vlib_node_runtime_t data for this node.
+ * @param frame vlib_frame_t whose contents should be dispatched.
+ *
+ * @par Graph mechanics: buffer metadata, next index usage
+ *
+ * <em>Uses:</em>
+ * - <code>node->runtime_data</code>
+ *     - Match tunnel by <code>rd->dev_instance</code> in IPSec-GRE tunnels
+ *       pool.
+ *
+ * <em>Sets:</em>
+ * - <code>vnet_buffer(b)->output_features.ipsec_sad_index</code>
+ *     - Set IPSec Security Association for packet encryption.
+ * - <code>vnet_buffer(b)->sw_if_index[VLIB_TX]</code>
+ *     - Reset output sw_if_index.
+ *
+ * <em>Nexd Index:</em>
+ * - Dispatches the packet to the esp-encrypt node.
+*/
+static uword
+ipsec_gre_interface_tx (vlib_main_t * vm,
+                       vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  ipsec_gre_main_t *igm = &ipsec_gre_main;
+  u32 next_index;
+  u32 *from, *to_next, n_left_from, n_left_to_next;
+  vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
+  ipsec_gre_tunnel_t *t = pool_elt_at_index (igm->tunnels, rd->dev_instance);
+
+  /* Vector of buffer / pkt indices we're supposed to process */
+  from = vlib_frame_vector_args (frame);
+
+  /* Number of buffers / pkts */
+  n_left_from = frame->n_vectors;
+
+  /* Speculatively send the first buffer to the last disposition we used */
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      /* set up to enqueue to our disposition with index = next_index */
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      /*
+       * As long as we have enough pkts left to process two pkts
+       * and prefetch two pkts...
+       */
+      while (n_left_from >= 4 && n_left_to_next >= 2)
+       {
+         vlib_buffer_t *b0, *b1;
+         ip4_header_t *ip0, *ip1;
+         ip4_and_gre_union_t *h0, *h1;
+         u32 bi0, next0, bi1, next1;
+         __attribute__ ((unused)) u8 error0, error1;
+         u16 gre_protocol0, gre_protocol1;
+
+         /* Prefetch the next iteration */
+         {
+           vlib_buffer_t *p2, *p3;
+
+           p2 = vlib_get_buffer (vm, from[2]);
+           p3 = vlib_get_buffer (vm, from[3]);
+
+           vlib_prefetch_buffer_header (p2, LOAD);
+           vlib_prefetch_buffer_header (p3, LOAD);
+
+           /*
+            * Prefetch packet data. We expect to overwrite
+            * the inbound L2 header with an ip header and a
+            * gre header. Might want to prefetch the last line
+            * of rewrite space as well; need profile data
+            */
+           CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
+           CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+         }
+
+         /* Pick up the next two buffer indices */
+         bi0 = from[0];
+         bi1 = from[1];
+
+         /* Speculatively enqueue them where we sent the last buffer */
+         to_next[0] = bi0;
+         to_next[1] = bi1;
+         from += 2;
+         to_next += 2;
+         n_left_to_next -= 2;
+         n_left_from -= 2;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+
+         ip0 = vlib_buffer_get_current (b0);
+         gre_protocol0 = clib_net_to_host_u16 (0x01);
+
+         ip1 = vlib_buffer_get_current (b1);
+         gre_protocol1 = clib_net_to_host_u16 (0x01);
+
+         vlib_buffer_advance (b0, -sizeof (*h0));
+         vlib_buffer_advance (b1, -sizeof (*h1));
+
+         h0 = vlib_buffer_get_current (b0);
+         h1 = vlib_buffer_get_current (b1);
+         h0->as_u64[0] = 0;
+         h0->as_u64[1] = 0;
+         h0->as_u64[2] = 0;
+
+         h1->as_u64[0] = 0;
+         h1->as_u64[1] = 0;
+         h1->as_u64[2] = 0;
+
+         ip0 = &h0->ip4_and_gre.ip4;
+         h0->ip4_and_gre.gre.protocol = gre_protocol0;
+         ip0->ip_version_and_header_length = 0x45;
+         ip0->ttl = 254;
+         ip0->protocol = IP_PROTOCOL_GRE;
+
+         ip1 = &h1->ip4_and_gre.ip4;
+         h1->ip4_and_gre.gre.protocol = gre_protocol1;
+         ip1->ip_version_and_header_length = 0x45;
+         ip1->ttl = 254;
+         ip1->protocol = IP_PROTOCOL_GRE;
+
+         ip0->length =
+           clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
+         ip1->length =
+           clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
+         ip0->src_address.as_u32 = t->tunnel_src.as_u32;
+         ip1->src_address.as_u32 = t->tunnel_src.as_u32;
+         ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
+         ip1->dst_address.as_u32 = t->tunnel_dst.as_u32;
+         ip0->checksum = ip4_header_checksum (ip0);
+         ip1->checksum = ip4_header_checksum (ip1);
+
+         vnet_buffer (b0)->output_features.ipsec_sad_index = t->local_sa;
+         vnet_buffer (b1)->output_features.ipsec_sad_index = t->local_sa;
+
+         vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
+         vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
+
+         next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
+         next1 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
+         error0 = IPSEC_GRE_ERROR_NONE;
+         error1 = IPSEC_GRE_ERROR_NONE;
+
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
+                                                        b0, sizeof (*tr));
+             tr->tunnel_id = t - igm->tunnels;
+             tr->length = ip0->length;
+             tr->src.as_u32 = ip0->src_address.as_u32;
+             tr->dst.as_u32 = ip0->dst_address.as_u32;
+             tr->sa_id = t->local_sa_id;
+           }
+
+         if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
+                                                        b1, sizeof (*tr));
+             tr->tunnel_id = t - igm->tunnels;
+             tr->length = ip1->length;
+             tr->src.as_u32 = ip1->src_address.as_u32;
+             tr->dst.as_u32 = ip1->dst_address.as_u32;
+             tr->sa_id = t->local_sa_id;
+           }
+
+         vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, bi1, next0, next1);
+       }
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         vlib_buffer_t *b0;
+         ip4_header_t *ip0;
+         ip4_and_gre_union_t *h0;
+         u32 bi0, next0;
+         __attribute__ ((unused)) u8 error0;
+         u16 gre_protocol0;
+
+         bi0 = to_next[0] = from[0];
+         from += 1;
+         n_left_from -= 1;
+         to_next += 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+         gre_protocol0 = clib_net_to_host_u16 (0x01);
+
+         vlib_buffer_advance (b0, -sizeof (*h0));
+
+         h0 = vlib_buffer_get_current (b0);
+         h0->as_u64[0] = 0;
+         h0->as_u64[1] = 0;
+         h0->as_u64[2] = 0;
+
+         ip0 = &h0->ip4_and_gre.ip4;
+         h0->ip4_and_gre.gre.protocol = gre_protocol0;
+         ip0->ip_version_and_header_length = 0x45;
+         ip0->ttl = 254;
+         ip0->protocol = IP_PROTOCOL_GRE;
+         ip0->length =
+           clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
+         ip0->src_address.as_u32 = t->tunnel_src.as_u32;
+         ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
+         ip0->checksum = ip4_header_checksum (ip0);
+
+         vnet_buffer (b0)->output_features.ipsec_sad_index = t->local_sa;
+         vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
+
+         next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
+         error0 = IPSEC_GRE_ERROR_NONE;
+
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
+                                                        b0, sizeof (*tr));
+             tr->tunnel_id = t - igm->tunnels;
+             tr->length = ip0->length;
+             tr->src.as_u32 = ip0->src_address.as_u32;
+             tr->dst.as_u32 = ip0->dst_address.as_u32;
+             tr->sa_id = t->local_sa_id;
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, ipsec_gre_input_node.index,
+                              IPSEC_GRE_ERROR_PKTS_ENCAP, frame->n_vectors);
+
+  return frame->n_vectors;
+}
+
+static clib_error_t *
+ipsec_gre_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index,
+                                  u32 flags)
+{
+  if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+    vnet_hw_interface_set_flags (vnm, hw_if_index,
+                                VNET_HW_INTERFACE_FLAG_LINK_UP);
+  else
+    vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */ );
+
+  return /* no error */ 0;
+}
+
+static u8 *
+format_ipsec_gre_tunnel_name (u8 * s, va_list * args)
+{
+  u32 dev_instance = va_arg (*args, u32);
+  return format (s, "ipsec-gre%d", dev_instance);
+}
+
+static u8 *
+format_ipsec_gre_device (u8 * s, va_list * args)
+{
+  u32 dev_instance = va_arg (*args, u32);
+  CLIB_UNUSED (int verbose) = va_arg (*args, int);
+
+  s = format (s, "IPSEC-GRE tunnel: id %d\n", dev_instance);
+  return s;
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (ipsec_gre_device_class) = {
+  .name = "IPSec GRE tunnel device",
+  .format_device_name = format_ipsec_gre_tunnel_name,
+  .format_device = format_ipsec_gre_device,
+  .format_tx_trace = format_ipsec_gre_tx_trace,
+  .tx_function = ipsec_gre_interface_tx,
+  .admin_up_down_function = ipsec_gre_interface_admin_up_down,
+};
+
+VLIB_DEVICE_TX_FUNCTION_MULTIARCH (ipsec_gre_device_class,
+                                  ipsec_gre_interface_tx)
+
+
+VNET_HW_INTERFACE_CLASS (ipsec_gre_hw_interface_class) = {
+  .name = "IPSEC-GRE",
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+ipsec_gre_init (vlib_main_t * vm)
+{
+  ipsec_gre_main_t *igm = &ipsec_gre_main;
+  clib_error_t *error;
+
+  memset (igm, 0, sizeof (igm[0]));
+  igm->vlib_main = vm;
+  igm->vnet_main = vnet_get_main ();
+
+  if ((error = vlib_call_init_function (vm, ip_main_init)))
+    return error;
+
+  if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
+    return error;
+
+  igm->tunnel_by_key = hash_create (0, sizeof (uword));
+
+  return vlib_call_init_function (vm, ipsec_gre_input_init);
+}
+
+VLIB_INIT_FUNCTION (ipsec_gre_init);
+
+ipsec_gre_main_t *
+ipsec_gre_get_main (vlib_main_t * vm)
+{
+  vlib_call_init_function (vm, ipsec_gre_init);
+  return &ipsec_gre_main;
+}
+
+/*
+* fd.io coding-style-patch-verification: ON
+*
+* Local Variables:
+* eval: (c-set-style "gnu")
+* End:
+*/
diff --git a/vnet/vnet/ipsec-gre/ipsec_gre.h b/vnet/vnet/ipsec-gre/ipsec_gre.h
new file mode 100644 (file)
index 0000000..2b66c6a
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+/**
+ * @file ipsec_gre.h
+ * @brief L2-GRE over IPSec packet processing.
+*/
+
+#ifndef included_ipsec_gre_h
+#define included_ipsec_gre_h
+
+#include <vnet/vnet.h>
+#include <vnet/gre/packet.h>
+#include <vnet/gre/gre.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ip/ip4.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/pg/pg.h>
+#include <vnet/ip/format.h>
+
+extern vnet_hw_interface_class_t ipsec_gre_hw_interface_class;
+
+/**
+ * @brief IPSec-GRE errors.
+ *
+*/
+typedef enum
+{
+#define ipsec_gre_error(n,s) IPSEC_GRE_ERROR_##n,
+#include <vnet/ipsec-gre/error.def>
+#undef ipsec_gre_error
+  IPSEC_GRE_N_ERROR,
+} ipsec_gre_error_t;
+
+/**
+ * @brief IPSec-GRE tunnel parameters.
+ *
+*/
+typedef struct
+{
+  ip4_address_t tunnel_src; /**< tunnel IPv4 src address */
+  ip4_address_t tunnel_dst; /**< tunnel IPv4 dst address */
+  u32 local_sa;                    /**< local IPSec SA index */
+  u32 remote_sa;           /**< remote IPSec SA index */
+  u32 local_sa_id;         /**< local IPSec SA id */
+  u32 remote_sa_id;        /**< remote IPSec SA id */
+  u32 hw_if_index;;        /**< hardware interface index */
+  u32 sw_if_index;;        /**< software interface index */
+} ipsec_gre_tunnel_t;
+
+/**
+ * @brief IPSec-GRE state.
+ *
+*/
+typedef struct
+{
+  ipsec_gre_tunnel_t *tunnels; /**< pool of tunnel instances */
+
+  uword *tunnel_by_key;         /**< hash mapping src/dst addr pair to tunnel */
+
+  u32 *free_ipsec_gre_tunnel_hw_if_indices;  /**< free vlib hw_if_indices */
+
+  u32 *tunnel_index_by_sw_if_index;  /**< mapping from sw_if_index to tunnel
+                                          index */
+
+  vlib_main_t *vlib_main;  /**< convenience */
+  vnet_main_t *vnet_main;  /**< convenience */
+} ipsec_gre_main_t;
+
+ipsec_gre_main_t ipsec_gre_main;
+
+extern vlib_node_registration_t ipsec_gre_input_node;
+extern vnet_device_class_t ipsec_gre_device_class;
+
+/* manually added to the interface output node in ipsec_gre.c */
+#define IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT 1
+
+/**
+ * @brief IPSec-GRE tunnel add/del arguments.
+ *
+*/
+typedef struct
+{
+  u8 is_add; /**< 1 - add, 0 - delete */
+
+  ip4_address_t src; /**< tunnel IPv4 src address */
+  ip4_address_t dst; /**< tunnel IPv4 dst address */
+  u32 lsa;          /**< local IPSec SA id */
+  u32 rsa;          /**< remote IPSec SA id */
+} vnet_ipsec_gre_add_del_tunnel_args_t;
+
+int vnet_ipsec_gre_add_del_tunnel
+  (vnet_ipsec_gre_add_del_tunnel_args_t * a, u32 * sw_if_indexp);
+
+#endif /* included_ipsec_gre_h */
+
+/*
+* fd.io coding-style-patch-verification: ON
+*
+* Local Variables:
+* eval: (c-set-style "gnu")
+* End:
+*/
diff --git a/vnet/vnet/ipsec-gre/ipsec_gre_doc.md b/vnet/vnet/ipsec-gre/ipsec_gre_doc.md
new file mode 100644 (file)
index 0000000..e9e5fdd
--- /dev/null
@@ -0,0 +1,74 @@
+# VPP L2-GRE over IPsec implementation
+
+This is a memo intended to contain documentation of the VPP L2-GRE over IPsec implementation.
+Everything that is not directly obvious should come here.
+
+
+## L2-GRE over IPsec
+GRE encapsulate layer 2 traffic and IPSec encrypt what is encapsulated by GRE. The whole point of L2-GRE over IPSec is to tunnel layer 2 over GRE and IPSec by bridging the physical interface with IPSec-GRE tunnel interface.
+
+There are 2 dedicated nodes for encapsulation:
+* ipsec-gre<n>-tx - add GRE header
+* esp-encrypt - encrypt GRE packet to ESP packet
+
+There are 3 dedicated nodes for decapsulation:
+* ipsec-if-input - match IPSec SA by source IP address and SPI in ESP packet
+* esp-decrypt - decrypt ESP packet
+* ipsec-gre-input - remove GRE header
+
+
+### Configuration
+
+L2-GRE over IPsec support the following CLI configuration command:
+    create ipsec gre tunnel src <addr> dst <addr> local-sa <id> remote-sa <id> [del]
+
+src: tunnel source IPv4 address
+dst: tunnel destination IPv4 address
+local-sa: tunnel local IPSec Security Association
+remote-sa: tunnel remote IPSec Security Association
+del: delete IPSec-GRE tunnel
+
+L2-GRE over IPsec support the following API configuration command:
+    ipsec_gre_add_del_tunnel src <addr> dst <addr> local_sa <sa-id> remote_sa <sa-id> [del]
+
+src: tunnel source IPv4 address
+dst: tunnel destination IPv4 address
+local_sa: tunnel local IPSec Security Association
+remote_sa: tunnel remote IPSec Security Association
+del: delete IPSec-GRE tunnel
+
+
+### Configuration example
+
+Interface GigabitEthernet0/9/0 is in bridge with ipsec-gre0 tunnel interface, interface GigabitEthernet0/8/0 sending encapsulated and encrypted traffic.
+
+Configure IPv4 address on sending interface:
+set int ip address GigabitEthernet0/8/0 192.168.1.1/24
+
+Configure IPSec Security Associations:
+ipsec sa add 10 spi 1001 esp crypto-key 4a506a794f574265564551694d653768 crypto-alg aes-cbc-128 integ-key 4339314b55523947594d6d3547666b45764e6a58 integ-alg sha1-96
+ipsec sa add 20 spi 1000 esp crypto-key 49517065716d6235726c734a4372466c crypto-alg aes-cbc-128 integ-key 307439636a5542735133595835546f68534e4f64 integ-alg sha1-96
+
+Create IPSec-GRE tunnel:
+create ipsec gre tunnel src 192.168.1.1 dst 192.168.1.2 local-sa 10 remote-sa 20
+
+Set interfaces state:
+set int state GigabitEthernet0/8/0 up
+set int state GigabitEthernet0/9/0 up
+set int state ipsec-gre0 up
+
+Bridge physical interface with IPSec-GRE tunnel interface:
+set interface l2 bridge GigabitEthernet0/9/0 1
+set interface l2 bridge ipsec-gre0 1
+
+
+### Operational data
+
+L2-GRE over IPsec support the following CLI show command:
+    show ipsec gre tunnel
+
+L2-GRE over IPsec support the following API dump command:
+    ipsec_gre_tunnel_dump [sw_if_index <nn>]
+
+sw_if_index: software interface index of the IPSec-GRE tunnel interface
+
diff --git a/vnet/vnet/ipsec-gre/node.c b/vnet/vnet/ipsec-gre/node.c
new file mode 100644 (file)
index 0000000..1310255
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * 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.
+ */
+/**
+ * @file node.c
+ * @brief L2-GRE over IPSec packet processing.
+ *
+ * Removes GRE header from the packet and send it to the l2-input node.
+*/
+
+#include <vlib/vlib.h>
+#include <vnet/pg/pg.h>
+#include <vnet/ipsec-gre/ipsec_gre.h>
+#include <vppinfra/sparse_vec.h>
+
+#define foreach_ipsec_gre_input_next           \
+_(PUNT, "error-punt")                           \
+_(DROP, "error-drop")                           \
+_(L2_INPUT, "l2-input")
+
+typedef enum {
+#define _(s,n) IPSEC_GRE_INPUT_NEXT_##s,
+  foreach_ipsec_gre_input_next
+#undef _
+  IPSEC_GRE_INPUT_N_NEXT,
+} ipsec_gre_input_next_t;
+
+typedef struct {
+  u32 tunnel_id;
+  u32 length;
+  ip4_address_t src;
+  ip4_address_t dst;
+} ipsec_gre_rx_trace_t;
+
+u8 * format_ipsec_gre_rx_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  ipsec_gre_rx_trace_t * t = va_arg (*args, ipsec_gre_rx_trace_t *);
+
+  s = format (s, "GRE: tunnel %d len %d src %U dst %U",
+              t->tunnel_id, clib_net_to_host_u16(t->length),
+              format_ip4_address, &t->src.as_u8,
+              format_ip4_address, &t->dst.as_u8);
+  return s;
+}
+
+/**
+ * @brief L2-GRE over IPSec input node.
+ * @node ipsec-gre-input
+ *
+ * This node remove GRE header.
+ *
+ * @param vm vlib_main_t corresponding to the current thread.
+ * @param node vlib_node_runtime_t data for this node.
+ * @param frame vlib_frame_t whose contents should be dispatched.
+ *
+ * @par Graph mechanics: buffer metadata, next index usage
+ *
+ * <em>Uses:<em>
+ * - <code>ip->src_address</code> and <code>ip->dst_address</code>
+ *     - Match tunnel by source and destination addresses in GRE IP header.
+ *
+ * <em>Sets:</em>
+ * - <code>vnet_buffer(b)->gre.src</code>
+ *     - Save tunnel source IPv4 address.
+ * - <code>vnet_buffer(b)->gre.dst</code>
+ *     - Save tunnel destination IPv4 address.
+ * - <code>vnet_buffer(b)->sw_if_index[VLIB_RX]</code>
+ *     - Set input sw_if_index to IPSec-GRE tunnel for learning.
+ *
+ * <em>Next Index:</em>
+ * - Dispatches the packet to the l2-input node.
+*/
+static uword
+ipsec_gre_input (vlib_main_t * vm,
+                 vlib_node_runtime_t * node,
+                 vlib_frame_t * from_frame)
+{
+  ipsec_gre_main_t * igm = &ipsec_gre_main;
+  u32 n_left_from, next_index, * from, * to_next;
+  u64 cached_tunnel_key = (u64) ~0;
+  u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index,
+                          to_next, n_left_to_next);
+
+      while (n_left_from >= 4 && n_left_to_next >= 2)
+       {
+         u32 bi0, bi1;
+         vlib_buffer_t * b0, * b1;
+         gre_header_t * h0, * h1;
+          u16 version0, version1, protocol0, protocol1;
+          int verr0, verr1;
+         u32 next0, next1;
+          ip4_header_t *ip0, *ip1;
+
+         /* Prefetch next iteration. */
+         {
+           vlib_buffer_t * p2, * p3;
+
+           p2 = vlib_get_buffer (vm, from[2]);
+           p3 = vlib_get_buffer (vm, from[3]);
+
+           vlib_prefetch_buffer_header (p2, LOAD);
+           vlib_prefetch_buffer_header (p3, LOAD);
+
+           CLIB_PREFETCH (p2->data, sizeof (h0[0]), LOAD);
+           CLIB_PREFETCH (p3->data, sizeof (h1[0]), LOAD);
+         }
+
+         bi0 = from[0];
+         bi1 = from[1];
+         to_next[0] = bi0;
+         to_next[1] = bi1;
+         from += 2;
+         to_next += 2;
+         n_left_to_next -= 2;
+         n_left_from -= 2;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+
+          /* ip4_local hands us the ip header, not the gre header */
+          ip0 = vlib_buffer_get_current (b0);
+          ip1 = vlib_buffer_get_current (b1);
+
+          /* Save src + dst ip4 address */
+          vnet_buffer(b0)->gre.src = ip0->src_address.as_u32;
+          vnet_buffer(b0)->gre.dst = ip0->dst_address.as_u32;
+          vnet_buffer(b1)->gre.src = ip1->src_address.as_u32;
+          vnet_buffer(b1)->gre.dst = ip1->dst_address.as_u32;
+
+          vlib_buffer_advance (b0, sizeof (*ip0));
+          vlib_buffer_advance (b1, sizeof (*ip1));
+
+         h0 = vlib_buffer_get_current (b0);
+         h1 = vlib_buffer_get_current (b1);
+
+          protocol0 = clib_net_to_host_u16 (h0->protocol);
+          protocol1 = clib_net_to_host_u16 (h1->protocol);
+          if (PREDICT_TRUE(protocol0 == 0x0001))
+            {
+              next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
+              b0->error = node->errors[IPSEC_GRE_ERROR_NONE];
+            }
+          else
+            {
+              clib_warning("unknown GRE protocol: %d", protocol0);
+              b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
+              next0 = IPSEC_GRE_INPUT_NEXT_DROP;
+            }
+          if (PREDICT_TRUE(protocol1 == 0x0001))
+            {
+              next1 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
+              b1->error = node->errors[IPSEC_GRE_ERROR_NONE];
+            }
+          else
+            {
+              clib_warning("unknown GRE protocol: %d", protocol1);
+              b1->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
+              next1 = IPSEC_GRE_INPUT_NEXT_DROP;
+            }
+
+          version0 = clib_net_to_host_u16 (h0->flags_and_version);
+          verr0 =  version0 & GRE_VERSION_MASK;
+          version1 = clib_net_to_host_u16 (h1->flags_and_version);
+          verr1 =  version1 & GRE_VERSION_MASK;
+
+          b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
+              : b0->error;
+          next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0;
+          b1->error = verr1 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
+              : b1->error;
+          next1 = verr1 ? IPSEC_GRE_INPUT_NEXT_DROP : next1;
+
+          /* For L2 payload set input sw_if_index to GRE tunnel for learning */
+          if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
+            {
+              u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) |
+                         (u64)(vnet_buffer(b0)->gre.src);
+
+              if (cached_tunnel_key != key)
+                {
+                  vnet_hw_interface_t * hi;
+                  ipsec_gre_tunnel_t * t;
+                  uword * p;
+
+                  p = hash_get (igm->tunnel_by_key, key);
+                  if (!p)
+                    {
+                      next0 = IPSEC_GRE_INPUT_NEXT_DROP;
+                      b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
+                      goto drop0;
+                    }
+                  t = pool_elt_at_index (igm->tunnels, p[0]);
+                  hi = vnet_get_hw_interface (igm->vnet_main,
+                            t->hw_if_index);
+                  tunnel_sw_if_index = hi->sw_if_index;
+                  cached_tunnel_sw_if_index = tunnel_sw_if_index;
+                }
+              else
+                {
+                  tunnel_sw_if_index = cached_tunnel_sw_if_index;
+                }
+              vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
+            }
+
+drop0:
+          /* For L2 payload set input sw_if_index to GRE tunnel for learning */
+          if (PREDICT_TRUE(next1 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
+            {
+              u64 key = ((u64)(vnet_buffer(b1)->gre.dst) << 32) |
+                         (u64)(vnet_buffer(b1)->gre.src);
+
+              if (cached_tunnel_key != key)
+                {
+                  vnet_hw_interface_t * hi;
+                  ipsec_gre_tunnel_t * t;
+                  uword * p;
+
+                  p = hash_get (igm->tunnel_by_key, key);
+                  if (!p)
+                    {
+                      next1 = IPSEC_GRE_INPUT_NEXT_DROP;
+                      b1->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
+                      goto drop1;
+                    }
+                  t = pool_elt_at_index (igm->tunnels, p[0]);
+                  hi = vnet_get_hw_interface (igm->vnet_main,
+                            t->hw_if_index);
+                  tunnel_sw_if_index = hi->sw_if_index;
+                  cached_tunnel_sw_if_index = tunnel_sw_if_index;
+                }
+              else
+                {
+                  tunnel_sw_if_index = cached_tunnel_sw_if_index;
+                }
+              vnet_buffer(b1)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
+            }
+
+drop1:
+          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
+            {
+              ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
+                                                   b0, sizeof (*tr));
+              tr->tunnel_id = ~0;
+              tr->length = ip0->length;
+              tr->src.as_u32 = ip0->src_address.as_u32;
+              tr->dst.as_u32 = ip0->dst_address.as_u32;
+            }
+
+          if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
+            {
+              ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
+                                                   b1, sizeof (*tr));
+              tr->tunnel_id = ~0;
+              tr->length = ip1->length;
+              tr->src.as_u32 = ip1->src_address.as_u32;
+              tr->dst.as_u32 = ip1->dst_address.as_u32;
+            }
+
+          vlib_buffer_advance (b0, sizeof (*h0));
+          vlib_buffer_advance (b1, sizeof (*h1));
+
+         vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, bi1, next0, next1);
+       }
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t * b0;
+         gre_header_t * h0;
+          ip4_header_t * ip0;
+          u16 version0, protocol0;
+          int verr0;
+         u32 next0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+          ip0 = vlib_buffer_get_current (b0);
+
+          vnet_buffer(b0)->gre.src = ip0->src_address.as_u32;
+          vnet_buffer(b0)->gre.dst = ip0->dst_address.as_u32;
+
+          vlib_buffer_advance (b0, sizeof (*ip0));
+
+         h0 = vlib_buffer_get_current (b0);
+
+          protocol0 = clib_net_to_host_u16 (h0->protocol);
+          if (PREDICT_TRUE(protocol0 == 0x0001))
+            {
+              next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
+              b0->error = node->errors[IPSEC_GRE_ERROR_NONE];
+            }
+          else
+            {
+              clib_warning("unknown GRE protocol: %d", protocol0);
+              b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
+              next0 = IPSEC_GRE_INPUT_NEXT_DROP;
+            }
+
+          version0 = clib_net_to_host_u16 (h0->flags_and_version);
+          verr0 =  version0 & GRE_VERSION_MASK;
+          b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
+              : b0->error;
+          next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0;
+
+          /* For L2 payload set input sw_if_index to GRE tunnel for learning */
+          if (PREDICT_FALSE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
+            {
+              u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) |
+                         (u64)(vnet_buffer(b0)->gre.src);
+
+              if (cached_tunnel_key != key)
+                {
+                  vnet_hw_interface_t * hi;
+                  ipsec_gre_tunnel_t * t;
+                  uword * p;
+
+                  p = hash_get (igm->tunnel_by_key, key);
+                  if (!p)
+                    {
+                      next0 = IPSEC_GRE_INPUT_NEXT_DROP;
+                      b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
+                      goto drop;
+                    }
+                  t = pool_elt_at_index (igm->tunnels, p[0]);
+                  hi = vnet_get_hw_interface (igm->vnet_main,
+                            t->hw_if_index);
+                  tunnel_sw_if_index = hi->sw_if_index;
+                  cached_tunnel_sw_if_index = tunnel_sw_if_index;
+                }
+              else
+                {
+                  tunnel_sw_if_index = cached_tunnel_sw_if_index;
+                }
+              vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
+            }
+
+drop:
+          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
+            {
+              ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
+                                                   b0, sizeof (*tr));
+              tr->tunnel_id = ~0;
+              tr->length = ip0->length;
+              tr->src.as_u32 = ip0->src_address.as_u32;
+              tr->dst.as_u32 = ip0->dst_address.as_u32;
+            }
+
+          vlib_buffer_advance (b0, sizeof (*h0));
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  vlib_node_increment_counter (vm, ipsec_gre_input_node.index,
+                               IPSEC_GRE_ERROR_PKTS_DECAP, from_frame->n_vectors);
+  return from_frame->n_vectors;
+}
+
+static char * ipsec_gre_error_strings[] = {
+#define ipsec_gre_error(n,s) s,
+#include "error.def"
+#undef ipsec_gre_error
+};
+
+VLIB_REGISTER_NODE (ipsec_gre_input_node) = {
+  .function = ipsec_gre_input,
+  .name = "ipsec-gre-input",
+  /* Takes a vector of packets. */
+  .vector_size = sizeof (u32),
+
+  .n_errors = IPSEC_GRE_N_ERROR,
+  .error_strings = ipsec_gre_error_strings,
+
+  .n_next_nodes = IPSEC_GRE_INPUT_N_NEXT,
+  .next_nodes = {
+#define _(s,n) [IPSEC_GRE_INPUT_NEXT_##s] = n,
+    foreach_ipsec_gre_input_next
+#undef _
+  },
+
+  .format_trace = format_ipsec_gre_rx_trace,
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ipsec_gre_input_node, ipsec_gre_input)
+
+static clib_error_t * ipsec_gre_input_init (vlib_main_t * vm)
+{
+  {
+    clib_error_t * error;
+    error = vlib_call_init_function (vm, ipsec_gre_init);
+    if (error)
+      clib_error_report (error);
+  }
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (ipsec_gre_input_init);
index c350508..6c7f27f 100644 (file)
@@ -27,7 +27,8 @@
 #define foreach_esp_decrypt_next                \
 _(DROP, "error-drop")                           \
 _(IP4_INPUT, "ip4-input")                       \
-_(IP6_INPUT, "ip6-input")
+_(IP6_INPUT, "ip6-input")                       \
+_(IPSEC_GRE_INPUT, "ipsec-gre-input")
 
 #define _(v, s) ESP_DECRYPT_NEXT_##v,
 typedef enum
@@ -421,7 +422,10 @@ esp_decrypt_node_fn (vlib_main_t * vm,
              if (PREDICT_TRUE (tunnel_mode))
                {
                  if (PREDICT_TRUE (f0->next_header == IP_PROTOCOL_IP_IN_IP))
-                   next0 = ESP_DECRYPT_NEXT_IP4_INPUT;
+                   {
+                     next0 = ESP_DECRYPT_NEXT_IP4_INPUT;
+                     oh4 = vlib_buffer_get_current (o_b0);
+                   }
                  else if (f0->next_header == IP_PROTOCOL_IPV6)
                    next0 = ESP_DECRYPT_NEXT_IP6_INPUT;
                  else
@@ -471,6 +475,12 @@ esp_decrypt_node_fn (vlib_main_t * vm,
                    }
                }
 
+             /* for IPSec-GRE tunnel next node is ipsec-gre-input */
+             if (PREDICT_FALSE
+                 ((vnet_buffer (i_b0)->output_features.ipsec_flags) &
+                  IPSEC_FLAG_IPSEC_GRE_TUNNEL))
+               next0 = ESP_DECRYPT_NEXT_IPSEC_GRE_INPUT;
+
              to_next[0] = o_bi0;
              to_next += 1;
 
index 45b4b3b..0516d43 100644 (file)
@@ -248,6 +248,8 @@ esp_encrypt_node_fn (vlib_main_t * vm,
              oh6_0->esp.spi = clib_net_to_host_u32 (sa0->spi);
              oh6_0->esp.seq = clib_net_to_host_u32 (sa0->seq);
              ip_proto = ih6_0->ip6.protocol;
+
+             next0 = ESP_ENCRYPT_NEXT_IP6_INPUT;
            }
          else
            {
@@ -268,6 +270,8 @@ esp_encrypt_node_fn (vlib_main_t * vm,
              oh0->esp.spi = clib_net_to_host_u32 (sa0->spi);
              oh0->esp.seq = clib_net_to_host_u32 (sa0->seq);
              ip_proto = ih0->ip4.protocol;
+
+             next0 = ESP_ENCRYPT_NEXT_IP4_INPUT;
            }
 
          if (PREDICT_TRUE
@@ -276,8 +280,6 @@ esp_encrypt_node_fn (vlib_main_t * vm,
              oh0->ip4.src_address.as_u32 = sa0->tunnel_src_addr.ip4.as_u32;
              oh0->ip4.dst_address.as_u32 = sa0->tunnel_dst_addr.ip4.as_u32;
 
-             /* in tunnel mode send it back to FIB */
-             next0 = ESP_ENCRYPT_NEXT_IP4_INPUT;
              vnet_buffer (o_b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
            }
          else if (is_ipv6 && sa0->is_tunnel && sa0->is_tunnel_ip6)
@@ -291,25 +293,26 @@ esp_encrypt_node_fn (vlib_main_t * vm,
              oh6_0->ip6.dst_address.as_u64[1] =
                sa0->tunnel_dst_addr.ip6.as_u64[1];
 
-             /* in tunnel mode send it back to FIB */
-             next0 = ESP_ENCRYPT_NEXT_IP6_INPUT;
              vnet_buffer (o_b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
            }
          else
            {
-             transport_mode = 1;
-             ethernet_header_t *ieh0, *oeh0;
-             ieh0 = (ethernet_header_t *) i_b0->data;
-             oeh0 = (ethernet_header_t *) o_b0->data;
-             clib_memcpy (oeh0, ieh0, sizeof (ethernet_header_t));
              vlib_buffer_advance (i_b0, ip_hdr_size);
              next_hdr_type = ip_proto;
-             next0 = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT;
-             o_b0->flags |= BUFFER_OUTPUT_FEAT_DONE;
-             vnet_buffer (o_b0)->sw_if_index[VLIB_TX] =
-               vnet_buffer (i_b0)->sw_if_index[VLIB_TX];
-             vnet_buffer (o_b0)->output_features.bitmap =
-               vnet_buffer (i_b0)->output_features.bitmap;
+             if (vnet_buffer (i_b0)->sw_if_index[VLIB_TX] != ~0)
+               {
+                 transport_mode = 1;
+                 ethernet_header_t *ieh0, *oeh0;
+                 ieh0 = (ethernet_header_t *) i_b0->data;
+                 oeh0 = (ethernet_header_t *) o_b0->data;
+                 clib_memcpy (oeh0, ieh0, sizeof (ethernet_header_t));
+                 next0 = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT;
+                 o_b0->flags |= BUFFER_OUTPUT_FEAT_DONE;
+                 vnet_buffer (o_b0)->sw_if_index[VLIB_TX] =
+                   vnet_buffer (i_b0)->sw_if_index[VLIB_TX];
+                 vnet_buffer (o_b0)->output_features.bitmap =
+                   vnet_buffer (i_b0)->output_features.bitmap;
+               }
            }
 
          ASSERT (sa0->crypto_alg < IPSEC_CRYPTO_N_ALG);
index bf9dd97..1b3b9ff 100644 (file)
 #include <vnet/ipsec/esp.h>
 #include <vnet/ipsec/ikev2.h>
 
+u32
+ipsec_get_sa_index_by_sa_id (u32 sa_id)
+{
+  ipsec_main_t *im = &ipsec_main;
+  uword *p = hash_get (im->sa_index_by_sa_id, sa_id);
+  if (!p)
+    return ~0;
+
+  return p[0];
+}
+
 int
 ipsec_set_interface_spd (vlib_main_t * vm, u32 sw_if_index, u32 spd_id,
                         int is_add)
@@ -391,6 +402,7 @@ ipsec_is_sa_used (u32 sa_index)
   ipsec_main_t *im = &ipsec_main;
   ipsec_spd_t *spd;
   ipsec_policy_t *p;
+  ipsec_tunnel_if_t *t;
 
   /* *INDENT-OFF* */
   pool_foreach(spd, im->spds, ({
@@ -402,6 +414,13 @@ ipsec_is_sa_used (u32 sa_index)
         }
     }));
   }));
+
+  pool_foreach(t, im->tunnel_interfaces, ({
+    if (t->input_sa_index == sa_index)
+      return 1;
+    if (t->output_sa_index == sa_index)
+      return 1;
+  }));
   /* *INDENT-ON* */
 
   return 0;
index 5b88c82..fd3e8a3 100644 (file)
@@ -16,6 +16,8 @@
 #include <vnet/devices/dpdk/dpdk.h>
 #endif
 
+#define IPSEC_FLAG_IPSEC_GRE_TUNNEL (1 << 0)
+
 #define foreach_ipsec_policy_action \
   _(0, BYPASS,  "bypass")          \
   _(1, DISCARD, "discard")         \
@@ -127,6 +129,15 @@ typedef struct
   u8 remote_integ_key[128];
 } ipsec_add_del_tunnel_args_t;
 
+typedef struct
+{
+  u8 is_add;
+  u32 local_sa_id;
+  u32 remote_sa_id;
+  ip4_address_t local_ip;
+  ip4_address_t remote_ip;
+} ipsec_add_del_ipsec_gre_tunnel_args_t;
+
 typedef enum
 {
   IPSEC_IF_SET_KEY_TYPE_NONE,
@@ -243,6 +254,7 @@ int ipsec_add_del_policy (vlib_main_t * vm, ipsec_policy_t * policy,
 int ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add);
 int ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update);
 
+u32 ipsec_get_sa_index_by_sa_id (u32 sa_id);
 u8 *format_ipsec_if_output_trace (u8 * s, va_list * args);
 u8 *format_ipsec_policy_action (u8 * s, va_list * args);
 u8 *format_ipsec_crypto_alg (u8 * s, va_list * args);
@@ -254,6 +266,9 @@ uword unformat_ipsec_integ_alg (unformat_input_t * input, va_list * args);
 
 /*u32 ipsec_add_del_tunnel_if (vnet_main_t * vnm, ipsec_add_del_tunnel_args_t * args); */
 int ipsec_add_del_tunnel_if (ipsec_add_del_tunnel_args_t * args);
+int ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm,
+                                   ipsec_add_del_ipsec_gre_tunnel_args_t *
+                                   args);
 int ipsec_set_interface_key (vnet_main_t * vnm, u32 hw_if_index,
                             ipsec_if_set_key_type_t type, u8 alg, u8 * key);
 
index 8b15110..785e040 100644 (file)
@@ -561,6 +561,8 @@ show_ipsec_command_fn (vlib_main_t * vm,
   vlib_cli_output (vm, "tunnel interfaces");
   /* *INDENT-OFF* */
   pool_foreach (t, im->tunnel_interfaces, ({
+    if (t->hw_if_index == ~0)
+      continue;
     hi = vnet_get_hw_interface (im->vnet_main, t->hw_if_index);
     vlib_cli_output(vm, "  %s seq", hi->name);
     sa = pool_elt_at_index(im->sad, t->output_sa_index);
index 475b7bd..f4fad8d 100644 (file)
@@ -194,6 +194,68 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm,
   return 0;
 }
 
+int
+ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm,
+                               ipsec_add_del_ipsec_gre_tunnel_args_t * args)
+{
+  ipsec_tunnel_if_t *t = 0;
+  ipsec_main_t *im = &ipsec_main;
+  uword *p;
+  ipsec_sa_t *sa;
+  u64 key;
+  u32 isa, osa;
+
+  p = hash_get (im->sa_index_by_sa_id, args->local_sa_id);
+  if (!p)
+    return VNET_API_ERROR_INVALID_VALUE;
+  isa = p[0];
+
+  p = hash_get (im->sa_index_by_sa_id, args->remote_sa_id);
+  if (!p)
+    return VNET_API_ERROR_INVALID_VALUE;
+  osa = p[0];
+  sa = pool_elt_at_index (im->sad, p[0]);
+
+  if (sa->is_tunnel)
+    key = (u64) sa->tunnel_dst_addr.ip4.as_u32 << 32 | (u64) sa->spi;
+  else
+    key = (u64) args->remote_ip.as_u32 << 32 | (u64) sa->spi;
+
+  p = hash_get (im->ipsec_if_pool_index_by_key, key);
+
+  if (args->is_add)
+    {
+      /* check if same src/dst pair exists */
+      if (p)
+       return VNET_API_ERROR_INVALID_VALUE;
+
+      pool_get_aligned (im->tunnel_interfaces, t, CLIB_CACHE_LINE_BYTES);
+      memset (t, 0, sizeof (*t));
+
+      t->input_sa_index = isa;
+      t->output_sa_index = osa;
+      t->hw_if_index = ~0;
+      hash_set (im->ipsec_if_pool_index_by_key, key,
+               t - im->tunnel_interfaces);
+
+      /*1st interface, register protocol */
+      if (pool_elts (im->tunnel_interfaces) == 1)
+       ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP,
+                              ipsec_if_input_node.index);
+    }
+  else
+    {
+      /* check if exists */
+      if (!p)
+       return VNET_API_ERROR_INVALID_VALUE;
+
+      t = pool_elt_at_index (im->tunnel_interfaces, p[0]);
+      hash_unset (im->ipsec_if_pool_index_by_key, key);
+      pool_put (im->tunnel_interfaces, t);
+    }
+  return 0;
+}
+
 int
 ipsec_set_interface_key (vnet_main_t * vnm, u32 hw_if_index,
                         ipsec_if_set_key_type_t type, u8 alg, u8 * key)
index e557442..07d4bf3 100644 (file)
@@ -113,6 +113,8 @@ ipsec_if_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
              t = pool_elt_at_index (im->tunnel_interfaces, p[0]);
              vnet_buffer (b0)->output_features.ipsec_sad_index =
                t->input_sa_index;
+             vnet_buffer (b0)->output_features.ipsec_flags =
+               t->hw_if_index == ~0 ? IPSEC_FLAG_IPSEC_GRE_TUNNEL : 0;
              vlib_buffer_advance (b0, ip4_header_bytes (ip0));
              next0 = IPSEC_IF_INPUT_NEXT_ESP_DECRYPT;
            }
index 2bc4e2b..8364e22 100644 (file)
@@ -239,11 +239,11 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm,
              ipsec_policy_t *p0;
              p0 = ipsec_input_protect_policy_match (spd0,
                                                     clib_net_to_host_u32
-                                                    (ip0->src_address.
-                                                     as_u32),
+                                                    (ip0->
+                                                     src_address.as_u32),
                                                     clib_net_to_host_u32
-                                                    (ip0->dst_address.
-                                                     as_u32),
+                                                    (ip0->
+                                                     dst_address.as_u32),
                                                     clib_net_to_host_u32
                                                     (esp0->spi));
 
@@ -253,6 +253,7 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm,
                  p0->counter.bytes += clib_net_to_host_u16 (ip0->length);
                  vnet_buffer (b0)->output_features.ipsec_sad_index =
                    p0->sa_index;
+                 vnet_buffer (b0)->output_features.ipsec_flags = 0;
                  next0 = IPSEC_INPUT_NEXT_ESP_DECRYPT;
                  vlib_buffer_advance (b0, ip4_header_bytes (ip0));
                  goto trace0;
@@ -382,6 +383,7 @@ VLIB_NODE_FUNCTION_MULTIARCH (ipsec_input_ip4_node, ipsec_input_ip4_node_fn)
                  p0->counter.bytes += header_size;
                  vnet_buffer (b0)->output_features.ipsec_sad_index =
                    p0->sa_index;
+                 vnet_buffer (b0)->output_features.ipsec_flags = 0;
                  next0 = IPSEC_INPUT_NEXT_ESP_DECRYPT;
                  vlib_buffer_advance (b0, header_size);
                  goto trace0;
index 4ad447c..c12b247 100644 (file)
@@ -3189,6 +3189,39 @@ static void vl_api_policer_classify_details_t_handler_json
   vat_json_object_add_uint (node, "table_index", ntohl (mp->table_index));
 }
 
+static void vl_api_ipsec_gre_add_del_tunnel_reply_t_handler
+  (vl_api_ipsec_gre_add_del_tunnel_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  i32 retval = ntohl (mp->retval);
+  if (vam->async_mode)
+    {
+      vam->async_errors += (retval < 0);
+    }
+  else
+    {
+      vam->retval = retval;
+      vam->sw_if_index = ntohl (mp->sw_if_index);
+      vam->result_ready = 1;
+    }
+}
+
+static void vl_api_ipsec_gre_add_del_tunnel_reply_t_handler_json
+  (vl_api_ipsec_gre_add_del_tunnel_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  vat_json_node_t node;
+
+  vat_json_init_object (&node);
+  vat_json_object_add_int (&node, "retval", ntohl (mp->retval));
+  vat_json_object_add_uint (&node, "sw_if_index", ntohl (mp->sw_if_index));
+
+  vat_json_print (vam->ofp, &node);
+  vat_json_free (&node);
+
+  vam->retval = ntohl (mp->retval);
+  vam->result_ready = 1;
+}
 
 #define vl_api_vnet_ip4_fib_counters_t_endian vl_noop_handler
 #define vl_api_vnet_ip4_fib_counters_t_print vl_noop_handler
@@ -3511,7 +3544,9 @@ _(PG_ENABLE_DISABLE_REPLY, pg_enable_disable_reply)                     \
 _(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL_REPLY,                         \
  ip_source_and_port_range_check_add_del_reply)                          \
 _(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL_REPLY,               \
- ip_source_and_port_range_check_interface_add_del_reply)
+ ip_source_and_port_range_check_interface_add_del_reply)                \
+_(IPSEC_GRE_ADD_DEL_TUNNEL_REPLY, ipsec_gre_add_del_tunnel_reply)       \
+_(IPSEC_GRE_TUNNEL_DETAILS, ipsec_gre_tunnel_details)
 
 /* M: construct, but don't yet send a message */
 
@@ -14917,6 +14952,133 @@ api_ip_source_and_port_range_check_interface_add_del (vat_main_t * vam)
   W;
 }
 
+static int
+api_ipsec_gre_add_del_tunnel (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_ipsec_gre_add_del_tunnel_t *mp;
+  f64 timeout;
+  u32 local_sa_id = 0;
+  u32 remote_sa_id = 0;
+  ip4_address_t src_address;
+  ip4_address_t dst_address;
+  u8 is_add = 1;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "local_sa %d", &local_sa_id))
+       ;
+      else if (unformat (i, "remote_sa %d", &remote_sa_id))
+       ;
+      else if (unformat (i, "src %U", unformat_ip4_address, &src_address))
+       ;
+      else if (unformat (i, "dst %U", unformat_ip4_address, &dst_address))
+       ;
+      else if (unformat (i, "del"))
+       is_add = 0;
+      else
+       {
+         clib_warning ("parse error '%U'", format_unformat_error, i);
+         return -99;
+       }
+    }
+
+  M (IPSEC_GRE_ADD_DEL_TUNNEL, ipsec_gre_add_del_tunnel);
+
+  mp->local_sa_id = ntohl (local_sa_id);
+  mp->remote_sa_id = ntohl (remote_sa_id);
+  clib_memcpy (mp->src_address, &src_address, sizeof (src_address));
+  clib_memcpy (mp->dst_address, &dst_address, sizeof (dst_address));
+  mp->is_add = is_add;
+
+  S;
+  W;
+  /* NOTREACHED */
+  return 0;
+}
+
+static void vl_api_ipsec_gre_tunnel_details_t_handler
+  (vl_api_ipsec_gre_tunnel_details_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+
+  fformat (vam->ofp, "%11d%15U%15U%14d%14d\n",
+          ntohl (mp->sw_if_index),
+          format_ip4_address, &mp->src_address,
+          format_ip4_address, &mp->dst_address,
+          ntohl (mp->local_sa_id), ntohl (mp->remote_sa_id));
+}
+
+static void vl_api_ipsec_gre_tunnel_details_t_handler_json
+  (vl_api_ipsec_gre_tunnel_details_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  vat_json_node_t *node = NULL;
+  struct in_addr ip4;
+
+  if (VAT_JSON_ARRAY != vam->json_tree.type)
+    {
+      ASSERT (VAT_JSON_NONE == vam->json_tree.type);
+      vat_json_init_array (&vam->json_tree);
+    }
+  node = vat_json_array_add (&vam->json_tree);
+
+  vat_json_init_object (node);
+  vat_json_object_add_uint (node, "sw_if_index", ntohl (mp->sw_if_index));
+  clib_memcpy (&ip4, &mp->src_address, sizeof (ip4));
+  vat_json_object_add_ip4 (node, "src_address", ip4);
+  clib_memcpy (&ip4, &mp->dst_address, sizeof (ip4));
+  vat_json_object_add_ip4 (node, "dst_address", ip4);
+  vat_json_object_add_uint (node, "local_sa_id", ntohl (mp->local_sa_id));
+  vat_json_object_add_uint (node, "remote_sa_id", ntohl (mp->remote_sa_id));
+}
+
+static int
+api_ipsec_gre_tunnel_dump (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_ipsec_gre_tunnel_dump_t *mp;
+  f64 timeout;
+  u32 sw_if_index;
+  u8 sw_if_index_set = 0;
+
+  /* Parse args required to build the message */
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "sw_if_index %d", &sw_if_index))
+       sw_if_index_set = 1;
+      else
+       break;
+    }
+
+  if (sw_if_index_set == 0)
+    {
+      sw_if_index = ~0;
+    }
+
+  if (!vam->json_output)
+    {
+      fformat (vam->ofp, "%11s%15s%15s%14s%14s\n",
+              "sw_if_index", "src_address", "dst_address",
+              "local_sa_id", "remote_sa_id");
+    }
+
+  /* Get list of gre-tunnel interfaces */
+  M (IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump);
+
+  mp->sw_if_index = htonl (sw_if_index);
+
+  S;
+
+  /* Use a control ping for synchronization */
+  {
+    vl_api_control_ping_t *mp;
+    M (CONTROL_PING, control_ping);
+    S;
+  }
+  W;
+}
+
 static int
 q_or_quit (vat_main_t * vam)
 {
@@ -15493,7 +15655,10 @@ _(ip_source_and_port_range_check_add_del,                               \
   "<ip-addr>/<mask> range <nn>-<nn> vrf <id>")                          \
 _(ip_source_and_port_range_check_interface_add_del,                     \
   "<intf> | sw_if_index <nn> [tcp-out-vrf <id>] [tcp-in-vrf <id>]"      \
-  "[udp-in-vrf <id>] [udp-out-vrf <id>]")
+  "[udp-in-vrf <id>] [udp-out-vrf <id>]")                               \
+_(ipsec_gre_add_del_tunnel,                                             \
+  "src <addr> dst <addr> local_sa <sa-id> remote_sa <sa-id> [del]")     \
+_(ipsec_gre_tunnel_dump, "[sw_if_index <nn>]")
 
 /* List of command functions, CLI names map directly to functions */
 #define foreach_cli_function                                    \
index 8b34fd9..e142be6 100644 (file)
@@ -82,6 +82,7 @@
 #include <vnet/policer/policer.h>
 #include <vnet/devices/netmap/netmap.h>
 #include <vnet/flow/flow_report.h>
+#include <vnet/ipsec-gre/ipsec_gre.h>
 
 #undef BIHASH_TYPE
 #undef __included_bihash_template_h__
@@ -382,7 +383,9 @@ _(PG_ENABLE_DISABLE, pg_enable_disable)                                 \
 _(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL,                               \
   ip_source_and_port_range_check_add_del)                               \
 _(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL,                     \
-  ip_source_and_port_range_check_interface_add_del)
+  ip_source_and_port_range_check_interface_add_del)                     \
+_(IPSEC_GRE_ADD_DEL_TUNNEL, ipsec_gre_add_del_tunnel)                   \
+_(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump)
 
 #define QUOTE_(x) #x
 #define QUOTE(x) QUOTE_(x)
@@ -7987,6 +7990,97 @@ reply:
   REPLY_MACRO (VL_API_IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL_REPLY);
 }
 
+static void
+vl_api_ipsec_gre_add_del_tunnel_t_handler (vl_api_ipsec_gre_add_del_tunnel_t *
+                                          mp)
+{
+  vl_api_ipsec_gre_add_del_tunnel_reply_t *rmp;
+  int rv = 0;
+  vnet_ipsec_gre_add_del_tunnel_args_t _a, *a = &_a;
+  u32 sw_if_index = ~0;
+
+  /* Check src & dst are different */
+  if (memcmp (mp->src_address, mp->dst_address, 4) == 0)
+    {
+      rv = VNET_API_ERROR_SAME_SRC_DST;
+      goto out;
+    }
+
+  memset (a, 0, sizeof (*a));
+
+  /* ip addresses sent in network byte order */
+  clib_memcpy (&(a->src), mp->src_address, 4);
+  clib_memcpy (&(a->dst), mp->dst_address, 4);
+  a->is_add = mp->is_add;
+  a->lsa = ntohl (mp->local_sa_id);
+  a->rsa = ntohl (mp->remote_sa_id);
+
+  rv = vnet_ipsec_gre_add_del_tunnel (a, &sw_if_index);
+
+out:
+    /* *INDENT-OFF* */
+    REPLY_MACRO2(VL_API_GRE_ADD_DEL_TUNNEL_REPLY,
+    ({
+        rmp->sw_if_index = ntohl (sw_if_index);
+    }));
+    /* *INDENT-ON* */
+}
+
+static void send_ipsec_gre_tunnel_details
+  (ipsec_gre_tunnel_t * t, unix_shared_memory_queue_t * q, u32 context)
+{
+  vl_api_ipsec_gre_tunnel_details_t *rmp;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id = ntohs (VL_API_IPSEC_GRE_TUNNEL_DETAILS);
+  clib_memcpy (rmp->src_address, &(t->tunnel_src), 4);
+  clib_memcpy (rmp->dst_address, &(t->tunnel_dst), 4);
+  rmp->sw_if_index = htonl (t->sw_if_index);
+  rmp->local_sa_id = htonl (t->local_sa_id);
+  rmp->remote_sa_id = htonl (t->remote_sa_id);
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void vl_api_ipsec_gre_tunnel_dump_t_handler
+  (vl_api_ipsec_gre_tunnel_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  ipsec_gre_main_t *igm = &ipsec_gre_main;
+  ipsec_gre_tunnel_t *t;
+  u32 sw_if_index;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    {
+      return;
+    }
+
+  sw_if_index = ntohl (mp->sw_if_index);
+
+  if (~0 == sw_if_index)
+    {
+        /* *INDENT-OFF* */
+        pool_foreach (t, igm->tunnels,
+        ({
+            send_ipsec_gre_tunnel_details(t, q, mp->context);
+        }));
+        /* *INDENT-ON* */
+    }
+  else
+    {
+      if ((sw_if_index >= vec_len (igm->tunnel_index_by_sw_if_index)) ||
+         (~0 == igm->tunnel_index_by_sw_if_index[sw_if_index]))
+       {
+         return;
+       }
+      t = &igm->tunnels[igm->tunnel_index_by_sw_if_index[sw_if_index]];
+      send_ipsec_gre_tunnel_details (t, q, mp->context);
+    }
+}
+
 #define BOUNCE_HANDLER(nn)                                              \
 static void vl_api_##nn##_t_handler (                                   \
     vl_api_##nn##_t *mp)                                                \
index c4d9a6e..db5ea64 100644 (file)
@@ -2534,6 +2534,42 @@ static void *vl_api_lisp_eid_table_dump_t_print
   FINISH;
 }
 
+static void *vl_api_ipsec_gre_add_del_tunnel_t_print
+  (vl_api_ipsec_gre_add_del_tunnel_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: ipsec_gre_add_del_tunnel ");
+
+  s = format (s, "dst %U ", format_ip4_address,
+             (ip4_address_t *) & (mp->dst_address));
+
+  s = format (s, "src %U ", format_ip4_address,
+             (ip4_address_t *) & (mp->src_address));
+
+  s = format (s, "local_sa %d ", ntohl (mp->local_sa_id));
+
+  s = format (s, "remote_sa %d ", ntohl (mp->remote_sa_id));
+
+  if (mp->is_add == 0)
+    s = format (s, "del ");
+
+  FINISH;
+}
+
+static void *vl_api_ipsec_gre_tunnel_dump_t_print
+  (vl_api_ipsec_gre_tunnel_dump_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: ipsec_gre_tunnel_dump ");
+
+  if (mp->sw_if_index != ~0)
+    s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index));
+
+  FINISH;
+}
+
 #define foreach_custom_print_no_arg_function                            \
 _(lisp_eid_table_map_dump)                                              \
 _(lisp_map_resolver_dump)                                               \
@@ -2677,7 +2713,10 @@ _(LISP_EID_TABLE_DUMP, lisp_eid_table_dump)                             \
 _(LISP_EID_TABLE_MAP_DUMP, lisp_eid_table_map_dump)                     \
 _(LISP_GPE_TUNNEL_DUMP, lisp_gpe_tunnel_dump)                           \
 _(LISP_MAP_RESOLVER_DUMP, lisp_map_resolver_dump)                       \
-_(LISP_LOCATOR_SET_DUMP, lisp_locator_set_dump)
+_(LISP_LOCATOR_SET_DUMP, lisp_locator_set_dump)                         \
+_(LISP_LOCATOR_SET_DUMP, lisp_locator_set_dump)                         \
+_(IPSEC_GRE_ADD_DEL_TUNNEL, ipsec_gre_add_del_tunnel)                   \
+_(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump)
   void
 vl_msg_api_custom_dump_configure (api_main_t * am)
 {
index 83f096b..0685d21 100644 (file)
@@ -4744,3 +4744,61 @@ define ip_source_and_port_range_check_interface_add_del_reply
   u32 context;
   i32 retval;
 };
+
+/** \brief Add / del ipsec gre tunnel request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param local_sa_id - local SA id
+    @param remote_sa_id - remote SA id
+    @param is_add - 1 if adding the tunnel, 0 if deleting
+    @param src_address - tunnel source address
+    @param dst_address - tunnel destination address
+*/
+define ipsec_gre_add_del_tunnel {
+    u32 client_index;
+    u32 context;
+    u32 local_sa_id;
+    u32 remote_sa_id;
+    u8 is_add;
+    u8 src_address[4];
+    u8 dst_address[4];
+};
+
+/** \brief Reply for add / del ipsec gre tunnel request
+    @param context - returned sender context, to match reply w/ request
+    @param retval - return code
+    @param sw_if_index - software index of the new ipsec gre tunnel
+*/
+define ipsec_gre_add_del_tunnel_reply {
+    u32 context;
+    i32 retval;
+    u32 sw_if_index;
+};
+
+/** \brief Dump ipsec gre tunnel table
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param tunnel_index - gre tunnel identifier or -1 in case of all tunnels
+*/
+define ipsec_gre_tunnel_dump {
+    u32 client_index;
+    u32 context;
+    u32 sw_if_index;
+};
+
+/** \brief mpls gre tunnel operational state response
+    @param context - returned sender context, to match reply w/ request
+    @param sw_if_index - software index of the ipsec gre tunnel
+    @param local_sa_id - local SA id
+    @param remote_sa_id - remote SA id
+    @param src_address - tunnel source address
+    @param dst_address - tunnel destination address
+*/
+define ipsec_gre_tunnel_details {
+    u32 context;
+    u32 sw_if_index;
+    u32 local_sa_id;
+    u32 remote_sa_id;
+    u8 src_address[4];
+    u8 dst_address[4];
+};