aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vnet/CMakeLists.txt1
-rw-r--r--src/vnet/ipsec/ipsec.api51
-rw-r--r--src/vnet/ipsec/ipsec_api.c42
-rw-r--r--src/vnet/ipsec/ipsec_format.c20
-rw-r--r--src/vnet/ipsec/ipsec_itf.c462
-rw-r--r--src/vnet/ipsec/ipsec_itf.h117
-rw-r--r--src/vnet/ipsec/ipsec_tun.c61
-rw-r--r--src/vnet/ipsec/ipsec_tun.h14
-rw-r--r--test/template_ipsec.py2
-rw-r--r--test/test_ipsec_tun_if_esp.py306
-rw-r--r--test/vpp_ipsec.py39
11 files changed, 1092 insertions, 23 deletions
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index 9ee0eeba070..e4182186e60 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -581,6 +581,7 @@ list(APPEND VNET_SOURCES
ipsec/ipsec_format.c
ipsec/ipsec_handoff.c
ipsec/ipsec_input.c
+ ipsec/ipsec_itf.c
ipsec/ipsec_punt.c
ipsec/ipsec_sa.c
ipsec/ipsec_spd.c
diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api
index dee9144dda1..488df1cc4ea 100644
--- a/src/vnet/ipsec/ipsec.api
+++ b/src/vnet/ipsec/ipsec.api
@@ -20,6 +20,7 @@ import "vnet/ipsec/ipsec_types.api";
import "vnet/interface_types.api";
import "vnet/ip/ip_types.api";
import "vnet/interface_types.api";
+import "vnet/tunnel/tunnel_types.api";
/** \brief IPsec: Add/delete Security Policy Database
@param client_index - opaque cookie to identify the sender
@@ -379,12 +380,60 @@ define ipsec_tunnel_if_add_del_reply {
vl_api_interface_index_t sw_if_index;
};
+typedef ipsec_itf
+{
+ u32 user_instance [default=0xffffffff];
+ vl_api_tunnel_mode_t mode;
+ vl_api_interface_index_t sw_if_index;
+};
+
+/** \brief Create an IPSec interface
+ */
+define ipsec_itf_create {
+ u32 client_index;
+ u32 context;
+ vl_api_ipsec_itf_t itf;
+};
+
+/** \brief Add IPsec interface interface response
+ @param context - sender context, to match reply w/ request
+ @param retval - return status
+ @param sw_if_index - sw_if_index of new interface (for successful add)
+*/
+define ipsec_itf_create_reply
+{
+ u32 context;
+ i32 retval;
+ vl_api_interface_index_t sw_if_index;
+};
+
+autoreply define ipsec_itf_delete
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+};
+
+define ipsec_itf_dump
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+};
+
+define ipsec_itf_details
+{
+ u32 context;
+ vl_api_ipsec_itf_t itf;
+};
+
/** \brief Dump IPsec security association
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param sa_id - optional ID of an SA to dump, if ~0 dump all SAs in SAD
*/
-define ipsec_sa_dump {
+define ipsec_sa_dump
+{
u32 client_index;
u32 context;
u32 sa_id;
diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c
index ef58f7a6010..667d9b2fd11 100644
--- a/src/vnet/ipsec/ipsec_api.c
+++ b/src/vnet/ipsec/ipsec_api.c
@@ -25,6 +25,7 @@
#include <vnet/ip/ip.h>
#include <vnet/ip/ip_types_api.h>
#include <vnet/ipsec/ipsec_types_api.h>
+#include <vnet/tunnel/tunnel_types_api.h>
#include <vnet/fib/fib.h>
#include <vnet/ipip/ipip.h>
@@ -33,6 +34,7 @@
#if WITH_LIBSSL > 0
#include <vnet/ipsec/ipsec.h>
#include <vnet/ipsec/ipsec_tun.h>
+#include <vnet/ipsec/ipsec_itf.h>
#endif /* IPSEC */
#define vl_typedefs /* define message structures */
@@ -60,6 +62,9 @@ _(IPSEC_SA_DUMP, ipsec_sa_dump) \
_(IPSEC_SPDS_DUMP, ipsec_spds_dump) \
_(IPSEC_SPD_DUMP, ipsec_spd_dump) \
_(IPSEC_SPD_INTERFACE_DUMP, ipsec_spd_interface_dump) \
+_(IPSEC_ITF_CREATE, ipsec_itf_create) \
+_(IPSEC_ITF_DELETE, ipsec_itf_delete) \
+_(IPSEC_ITF_DUMP, ipsec_itf_dump) \
_(IPSEC_TUNNEL_IF_ADD_DEL, ipsec_tunnel_if_add_del) \
_(IPSEC_TUNNEL_IF_SET_SA, ipsec_tunnel_if_set_sa) \
_(IPSEC_SELECT_BACKEND, ipsec_select_backend) \
@@ -736,6 +741,43 @@ done:
/* *INDENT-ON* */
}
+static void
+vl_api_ipsec_itf_create_t_handler (vl_api_ipsec_itf_create_t * mp)
+{
+ vl_api_ipsec_itf_create_reply_t *rmp;
+ tunnel_mode_t mode;
+ u32 sw_if_index = ~0;
+ int rv;
+
+ rv = tunnel_mode_decode (mp->itf.mode, &mode);
+
+ if (!rv)
+ rv = ipsec_itf_create (ntohl (mp->itf.user_instance), mode, &sw_if_index);
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_IPSEC_ITF_CREATE_REPLY,
+ ({
+ rmp->sw_if_index = htonl (sw_if_index);
+ }));
+ /* *INDENT-ON* */
+}
+
+static void
+vl_api_ipsec_itf_delete_t_handler (vl_api_ipsec_itf_delete_t * mp)
+{
+ vl_api_ipsec_itf_delete_reply_t *rmp;
+ int rv;
+
+ rv = ipsec_itf_delete (ntohl (mp->sw_if_index));
+
+ REPLY_MACRO (VL_API_IPSEC_ITF_DELETE_REPLY);
+}
+
+static void
+vl_api_ipsec_itf_dump_t_handler (vl_api_ipsec_itf_dump_t * mp)
+{
+}
+
typedef struct ipsec_sa_dump_match_ctx_t_
{
index_t sai;
diff --git a/src/vnet/ipsec/ipsec_format.c b/src/vnet/ipsec/ipsec_format.c
index 98de779b0de..e3c6f22bf00 100644
--- a/src/vnet/ipsec/ipsec_format.c
+++ b/src/vnet/ipsec/ipsec_format.c
@@ -353,6 +353,21 @@ format_ipsec_tun_protect_index (u8 * s, va_list * args)
return (format (s, "%U", format_ipsec_tun_protect, itp));
}
+u8 *
+format_ipsec_tun_protect_flags (u8 * s, va_list * args)
+{
+ ipsec_protect_flags_t flags = va_arg (*args, int);
+
+ if (IPSEC_PROTECT_NONE == flags)
+ s = format (s, "none");
+#define _(a,b,c) \
+ else if (flags & IPSEC_PROTECT_##a) \
+ s = format (s, "%s", c); \
+ foreach_ipsec_protect_flags
+#undef _
+
+ return (s);
+}
u8 *
format_ipsec_tun_protect (u8 * s, va_list * args)
@@ -360,8 +375,9 @@ format_ipsec_tun_protect (u8 * s, va_list * args)
ipsec_tun_protect_t *itp = va_arg (*args, ipsec_tun_protect_t *);
u32 sai;
- s = format (s, "%U", format_vnet_sw_if_index_name,
- vnet_get_main (), itp->itp_sw_if_index);
+ s = format (s, "%U flags:[%U]", format_vnet_sw_if_index_name,
+ vnet_get_main (), itp->itp_sw_if_index,
+ format_ipsec_tun_protect_flags, itp->itp_flags);
if (!ip_address_is_zero (itp->itp_key))
s = format (s, ": %U", format_ip_address, itp->itp_key);
s = format (s, "\n output-sa:");
diff --git a/src/vnet/ipsec/ipsec_itf.c b/src/vnet/ipsec/ipsec_itf.c
new file mode 100644
index 00000000000..756bc19fbef
--- /dev/null
+++ b/src/vnet/ipsec/ipsec_itf.c
@@ -0,0 +1,462 @@
+/*
+ * ipsec_itf.c: IPSec dedicated interface type
+ *
+ * Copyright (c) 2020 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/ip/ip.h>
+#include <vnet/ipsec/ipsec_itf.h>
+#include <vnet/ipsec/ipsec_tun.h>
+#include <vnet/ipsec/ipsec.h>
+#include <vnet/adj/adj_midchain.h>
+
+/* bitmap of Allocated IPSEC_ITF instances */
+static uword *ipsec_itf_instances;
+
+/* pool of interfaces */
+static ipsec_itf_t *ipsec_itf_pool;
+
+static u32 *ipsec_itf_index_by_sw_if_index;
+
+static ipsec_itf_t *
+ipsec_itf_find_by_sw_if_index (u32 sw_if_index)
+{
+ if (vec_len (ipsec_itf_index_by_sw_if_index) <= sw_if_index)
+ return NULL;
+ u32 ti = ipsec_itf_index_by_sw_if_index[sw_if_index];
+ if (ti == ~0)
+ return NULL;
+ return pool_elt_at_index (ipsec_itf_pool, ti);
+}
+
+static u8 *
+format_ipsec_itf_name (u8 * s, va_list * args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+ return format (s, "ipsec%d", dev_instance);
+}
+
+void
+ipsec_itf_adj_unstack (adj_index_t ai)
+{
+ adj_midchain_delegate_unstack (ai);
+}
+
+void
+ipsec_itf_adj_stack (adj_index_t ai, u32 sai)
+{
+ const vnet_hw_interface_t *hw;
+
+ hw = vnet_get_sup_hw_interface (vnet_get_main (), adj_get_sw_if_index (ai));
+
+ if (hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
+ {
+ const ipsec_sa_t *sa;
+
+ sa = ipsec_sa_get (sai);
+
+ /* *INDENT-OFF* */
+ const fib_prefix_t dst = {
+ .fp_len = (ipsec_sa_is_set_IS_TUNNEL_V6(sa) ? 128 : 32),
+ .fp_proto = (ipsec_sa_is_set_IS_TUNNEL_V6(sa)?
+ FIB_PROTOCOL_IP6 :
+ FIB_PROTOCOL_IP4),
+ .fp_addr = sa->tunnel_dst_addr,
+ };
+ /* *INDENT-ON* */
+
+ adj_midchain_delegate_stack (ai, 0, &dst);
+ }
+ else
+ adj_midchain_delegate_unstack (ai);
+}
+
+static adj_walk_rc_t
+ipsec_itf_adj_stack_cb (adj_index_t ai, void *arg)
+{
+ ipsec_tun_protect_t *itp = arg;
+
+ ipsec_itf_adj_stack (ai, itp->itp_out_sa);
+
+ return (ADJ_WALK_RC_CONTINUE);
+}
+
+static void
+ipsec_itf_restack (index_t itpi, const ipsec_itf_t * itf)
+{
+ ipsec_tun_protect_t *itp;
+ fib_protocol_t proto;
+
+ itp = ipsec_tun_protect_get (itpi);
+
+ /*
+ * walk all the adjacencies on the interface and restack them
+ */
+ FOR_EACH_FIB_IP_PROTOCOL (proto)
+ {
+ adj_nbr_walk (itf->ii_sw_if_index, proto, ipsec_itf_adj_stack_cb, itp);
+ }
+}
+
+static walk_rc_t
+ipsec_tun_protect_walk_state_change (index_t itpi, void *arg)
+{
+ const ipsec_itf_t *itf = arg;
+
+ ipsec_itf_restack (itpi, itf);
+
+ return (WALK_CONTINUE);
+}
+
+static clib_error_t *
+ipsec_itf_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+ vnet_hw_interface_t *hi;
+ ipsec_itf_t *itf;
+ u32 hw_flags;
+
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+ hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
+ VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
+ vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
+
+ itf = ipsec_itf_find_by_sw_if_index (hi->sw_if_index);
+
+ if (itf)
+ ipsec_tun_protect_walk_itf (itf->ii_sw_if_index,
+ ipsec_tun_protect_walk_state_change, itf);
+
+ return (NULL);
+}
+
+static int
+ipsec_itf_tunnel_desc (u32 sw_if_index,
+ ip46_address_t * src, ip46_address_t * dst, u8 * is_l2)
+{
+ ip46_address_reset (src);
+ ip46_address_reset (dst);
+ *is_l2 = 0;
+
+ return (0);
+}
+
+static u8 *
+ipsec_itf_build_rewrite (void)
+{
+ /*
+ * passing the adj code a NULL rewrite means 'i don't have one cos
+ * t'other end is unresolved'. That's not the case here. For the ipsec
+ * tunnel there are just no bytes of encap to apply in the adj.
+ * So return a zero length rewrite. Encap will be added by a tunnel mode SA.
+ */
+ u8 *rewrite = NULL;
+
+ vec_validate (rewrite, 0);
+ vec_reset_length (rewrite);
+
+ return (rewrite);
+}
+
+static u8 *
+ipsec_itf_build_rewrite_i (vnet_main_t * vnm,
+ u32 sw_if_index,
+ vnet_link_t link_type, const void *dst_address)
+{
+ return (ipsec_itf_build_rewrite ());
+}
+
+void
+ipsec_itf_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
+{
+ adj_nbr_midchain_update_rewrite
+ (ai, NULL, NULL, ADJ_FLAG_MIDCHAIN_IP_STACK, ipsec_itf_build_rewrite ());
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (ipsec_itf_device_class) = {
+ .name = "IPSEC Tunnel",
+ .format_device_name = format_ipsec_itf_name,
+ .admin_up_down_function = ipsec_itf_admin_up_down,
+ .ip_tun_desc = ipsec_itf_tunnel_desc,
+};
+
+VNET_HW_INTERFACE_CLASS(ipsec_hw_interface_class) = {
+ .name = "IPSec",
+ .build_rewrite = ipsec_itf_build_rewrite_i,
+ .update_adjacency = ipsec_itf_update_adj,
+ .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
+};
+/* *INDENT-ON* */
+
+/*
+ * Maintain a bitmap of allocated ipsec_itf instance numbers.
+ */
+#define IPSEC_ITF_MAX_INSTANCE (16 * 1024)
+
+static u32
+ipsec_itf_instance_alloc (u32 want)
+{
+ /*
+ * Check for dynamically allocated instance number.
+ */
+ if (~0 == want)
+ {
+ u32 bit;
+
+ bit = clib_bitmap_first_clear (ipsec_itf_instances);
+ if (bit >= IPSEC_ITF_MAX_INSTANCE)
+ {
+ return ~0;
+ }
+ ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, bit, 1);
+ return bit;
+ }
+
+ /*
+ * In range?
+ */
+ if (want >= IPSEC_ITF_MAX_INSTANCE)
+ {
+ return ~0;
+ }
+
+ /*
+ * Already in use?
+ */
+ if (clib_bitmap_get (ipsec_itf_instances, want))
+ {
+ return ~0;
+ }
+
+ /*
+ * Grant allocation request.
+ */
+ ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, want, 1);
+
+ return want;
+}
+
+static int
+ipsec_itf_instance_free (u32 instance)
+{
+ if (instance >= IPSEC_ITF_MAX_INSTANCE)
+ {
+ return -1;
+ }
+
+ if (clib_bitmap_get (ipsec_itf_instances, instance) == 0)
+ {
+ return -1;
+ }
+
+ ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, instance, 0);
+ return 0;
+}
+
+int
+ipsec_itf_create (u32 user_instance, tunnel_mode_t mode, u32 * sw_if_indexp)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 instance, hw_if_index;
+ vnet_hw_interface_t *hi;
+ ipsec_itf_t *ipsec_itf;
+
+ ASSERT (sw_if_indexp);
+
+ *sw_if_indexp = (u32) ~ 0;
+
+ if (mode != TUNNEL_MODE_P2P)
+ return VNET_API_ERROR_UNSUPPORTED;
+
+ /*
+ * Allocate a ipsec_itf instance. Either select on dynamically
+ * or try to use the desired user_instance number.
+ */
+ instance = ipsec_itf_instance_alloc (user_instance);
+ if (instance == ~0)
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+
+ pool_get (ipsec_itf_pool, ipsec_itf);
+
+ /* tunnel index (or instance) */
+ u32 t_idx = ipsec_itf - ipsec_itf_pool;
+
+ ipsec_itf->ii_mode = mode;
+ ipsec_itf->ii_user_instance = instance;
+ if (~0 == ipsec_itf->ii_user_instance)
+ ipsec_itf->ii_user_instance = t_idx;
+
+ hw_if_index = vnet_register_interface (vnm,
+ ipsec_itf_device_class.index,
+ t_idx,
+ ipsec_hw_interface_class.index,
+ t_idx);
+
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+
+ vec_validate_init_empty (ipsec_itf_index_by_sw_if_index, hi->sw_if_index,
+ INDEX_INVALID);
+ ipsec_itf_index_by_sw_if_index[hi->sw_if_index] = t_idx;
+
+ ipsec_itf->ii_sw_if_index = *sw_if_indexp = hi->sw_if_index;
+
+ return 0;
+}
+
+int
+ipsec_itf_delete (u32 sw_if_index)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+
+ if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
+ if (hw == 0 || hw->dev_class_index != ipsec_itf_device_class.index)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ ipsec_itf_t *ipsec_itf;
+ ipsec_itf = ipsec_itf_find_by_sw_if_index (sw_if_index);
+ if (NULL == ipsec_itf)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ if (ipsec_itf_instance_free (hw->dev_instance) < 0)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ vnet_delete_hw_interface (vnm, hw->hw_if_index);
+ pool_put (ipsec_itf_pool, ipsec_itf);
+
+ return 0;
+}
+
+static clib_error_t *
+ipsec_itf_create_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u32 instance, sw_if_index;
+ clib_error_t *error;
+ mac_address_t mac;
+ int rv;
+
+ error = NULL;
+ instance = sw_if_index = ~0;
+ mac_address_set_zero (&mac);
+
+ if (unformat_user (input, unformat_line_input, line_input))
+ {
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "instance %d", &instance))
+ ;
+ else
+ {
+ error = clib_error_return (0, "unknown input: %U",
+ format_unformat_error, line_input);
+ break;
+ }
+ }
+
+ unformat_free (line_input);
+
+ if (error)
+ return error;
+ }
+
+ rv = ipsec_itf_create (instance, TUNNEL_MODE_P2P, &sw_if_index);
+
+ if (rv)
+ return clib_error_return (0, "iPSec interface create failed");
+
+ vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index);
+ return 0;
+}
+
+/*?
+ * Create a IPSec interface.
+ *
+ * @cliexpar
+ * The following two command syntaxes are equivalent:
+ * @cliexcmd{ipsec itf create [instance <instance>]}
+ * Example of how to create a ipsec interface:
+ * @cliexcmd{ipsec itf create}
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (ipsec_itf_create_command, static) = {
+ .path = "ipsec itf create",
+ .short_help = "ipsec itf create [instance <instance>]",
+ .function = ipsec_itf_create_cli,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+ipsec_itf_delete_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm;
+ u32 sw_if_index;
+ int rv;
+
+ vnm = vnet_get_main ();
+ sw_if_index = ~0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat
+ (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
+ ;
+ else
+ break;
+ }
+
+ if (~0 != sw_if_index)
+ {
+ rv = ipsec_itf_delete (sw_if_index);
+
+ if (rv)
+ return clib_error_return (0, "ipsec interface delete failed");
+ }
+ else
+ return clib_error_return (0, "no such interface: %U",
+ format_unformat_error, input);
+
+ return 0;
+}
+
+/*?
+ * Delete a IPSEC_ITF interface.
+ *
+ * @cliexpar
+ * The following two command syntaxes are equivalent:
+ * @cliexcmd{ipsec itf delete <interface>}
+ * Example of how to create a ipsec_itf interface:
+ * @cliexcmd{ipsec itf delete ipsec0}
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (ipsec_itf_delete_command, static) = {
+ .path = "ipsec itf delete",
+ .short_help = "ipsec itf delete <interface>",
+ .function = ipsec_itf_delete_cli,
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ipsec/ipsec_itf.h b/src/vnet/ipsec/ipsec_itf.h
new file mode 100644
index 00000000000..93e03f7b477
--- /dev/null
+++ b/src/vnet/ipsec/ipsec_itf.h
@@ -0,0 +1,117 @@
+/*
+ * ipsec_itf.c: IPSec dedicated interface type
+ *
+ * Copyright (c) 2020 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.
+ */
+
+#ifndef __IPSEC_ITF_H__
+#define __IPSEC_ITF_H__
+
+#include <vnet/tunnel/tunnel.h>
+#include <vnet/ipsec/ipsec_sa.h>
+
+/**
+ * @brief A dedicated IPSec interface type
+ *
+ * In order to support route based VPNs one needs 3 elements: an interface,
+ * for routing to resolve routes through, an SA from the peer to describe
+ * security, and encap, to describe how to reach the peer. There are two
+ * ways one could model this:
+ *
+ * interface + encap + SA = (interface + encap) + SA =
+ * ipip-interface + SA transport mode
+ *
+ * or
+ *
+ * interface + encap + SA = interface + (encap + SA) =
+ * IPSec-interface + SA tunnel mode
+ *
+ * It's a question of where you add the parenthesis, from the perspective
+ * of the external user the effect is identical.
+ *
+ * The IPsec interface serves as the encap-free interface to be used
+ * in conjunction with an encap-describing tunnel mode SA.
+ *
+ * VPP supports both models, which modelshould you pick?
+ * A route based VPN could impose 0, 1 or 2 encaps. the support matrix for
+ * these use cases is:
+ *
+ * | 0 | 1 | 2 |
+ * --------------------------
+ * ipip | N | Y | Y |
+ * ipsec | P | Y | P |
+ *
+ * Where P = potentially.
+ * ipsec could potnetially support 0 encap (i.e. transport mode) since neither
+ * the interface nor the SA *requires* encap. However, for a route beased VPN
+ * to use transport mode is probably wrong since one shouldn't use thransport
+ * mode for transit traffic, since without encap it is not guaranteed to return.
+ * ipsec could potnetially support 2 encaps, but that would require the SA to
+ * describe both, something it does not do at this time.
+ *
+ * ipsec currently does not support:
+ * - multipoint interfaces
+ * but this is only because it is not yet implemented, rather than it cannot
+ * be done.
+ *
+ * Internally the difference is that the midchain adjacency for the IPSec
+ * interface has no associated encap (whereas for an ipip tunnel it describes
+ * the peer). Consequently, features on the output arc see packets without
+ * any encap. Since the protecting SAs are in tunnel mode,
+ * they apply the encap. The midchain adj is stacked only once the proctecting
+ * SA is known, since only then is the peer known. Otherwise the VLIB graph
+ * nodes used are the same:
+ * (routing) --> ipX-michain --> espX-encrypt --> adj-midchain-tx --> (routing)
+ * where X = 4 or 6.
+ *
+ * Some benefits to the ipsec interface:
+ * - it is slightly more efficient since the encapsulating IP header has
+ * its checksum updated only once.
+ * - even when the interface is admin up traffic cannot be sent to a peer
+ * unless the SA is available (since it's the SA that determines the
+ * encap). With ipip interfaces a client must use the admin state to
+ * prevent sending until the SA is available.
+ *
+ * The best recommendations i can make are:
+ * - pick a model that supports your use case
+ * - make sure any other features you wish to use are supported by the model
+ * - choose the model that best fits your control plane's model.
+ *
+ *
+ * gun reloaded, fire away.
+ */
+typedef struct ipsec_itf_t_
+{
+ tunnel_mode_t ii_mode;
+ int ii_user_instance;
+ u32 ii_sw_if_index;
+} __clib_packed ipsec_itf_t;
+
+
+extern int ipsec_itf_create (u32 user_instance,
+ tunnel_mode_t mode, u32 * sw_if_indexp);
+extern int ipsec_itf_delete (u32 sw_if_index);
+
+extern void ipsec_itf_adj_stack (adj_index_t ai, u32 sai);
+extern void ipsec_itf_adj_unstack (adj_index_t ai);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
+
+#endif
diff --git a/src/vnet/ipsec/ipsec_tun.c b/src/vnet/ipsec/ipsec_tun.c
index 9b76d311bc2..fb530c8dc9f 100644
--- a/src/vnet/ipsec/ipsec_tun.c
+++ b/src/vnet/ipsec/ipsec_tun.c
@@ -16,6 +16,7 @@
*/
#include <vnet/ipsec/ipsec_tun.h>
+#include <vnet/ipsec/ipsec_itf.h>
#include <vnet/ipsec/esp.h>
#include <vnet/udp/udp.h>
#include <vnet/adj/adj_delegate.h>
@@ -126,14 +127,20 @@ ipsec_tun_protect_from_const_base (const adj_delegate_t * ad)
}
static u32
-ipsec_tun_protect_get_adj_next (const ipsec_tun_protect_t * itp)
+ipsec_tun_protect_get_adj_next (vnet_link_t linkt,
+ const ipsec_tun_protect_t * itp)
{
ipsec_main_t *im;
ipsec_sa_t *sa;
bool is_ip4;
u32 next;
- is_ip4 = ip46_address_is_ip4 (&itp->itp_tun.src);
+
+ if (itp->itp_flags & IPSEC_PROTECT_ITF)
+ is_ip4 = linkt == VNET_LINK_IP4;
+ else
+ is_ip4 = ip46_address_is_ip4 (&itp->itp_tun.src);
+
sa = ipsec_sa_get (itp->itp_out_sa);
im = &ipsec_main;
@@ -169,7 +176,7 @@ ipsec_tun_protect_add_adj (adj_index_t ai, const ipsec_tun_protect_t * itp)
{
ipsec_tun_protect_sa_by_adj_index[ai] = itp->itp_out_sa;
adj_nbr_midchain_update_next_node
- (ai, ipsec_tun_protect_get_adj_next (itp));
+ (ai, ipsec_tun_protect_get_adj_next (adj_get_link_type (ai), itp));
}
}
@@ -249,6 +256,9 @@ ipsec_tun_protect_adj_add (adj_index_t ai, void *arg)
itp - ipsec_tun_protect_pool);
ipsec_tun_protect_add_adj (ai, itp);
+ if (itp->itp_flags & IPSEC_PROTECT_ITF)
+ ipsec_itf_adj_stack (ai, itp->itp_out_sa);
+
return (ADJ_WALK_RC_CONTINUE);
}
@@ -349,9 +359,14 @@ ipsec_tun_protect_rx_db_remove (ipsec_main_t * im,
static adj_walk_rc_t
ipsec_tun_protect_adj_remove (adj_index_t ai, void *arg)
{
+ ipsec_tun_protect_t *itp = arg;
+
adj_delegate_remove (ai, ipsec_tun_adj_delegate_type);
ipsec_tun_protect_add_adj (ai, NULL);
+ if (itp->itp_flags & IPSEC_PROTECT_ITF)
+ ipsec_itf_adj_unstack (ai);
+
return (ADJ_WALK_RC_CONTINUE);
}
@@ -404,8 +419,11 @@ ipsec_tun_protect_set_crypto_addr (ipsec_tun_protect_t * itp)
{
itp->itp_crypto.src = sa->tunnel_dst_addr;
itp->itp_crypto.dst = sa->tunnel_src_addr;
- ipsec_sa_set_IS_PROTECT (sa);
- itp->itp_flags |= IPSEC_PROTECT_ENCAPED;
+ if (!(itp->itp_flags & IPSEC_PROTECT_ITF))
+ {
+ ipsec_sa_set_IS_PROTECT (sa);
+ itp->itp_flags |= IPSEC_PROTECT_ENCAPED;
+ }
}
else
{
@@ -657,6 +675,7 @@ ipsec_tun_protect_update (u32 sw_if_index,
pool_get_zero (ipsec_tun_protect_pool, itp);
itp->itp_sw_if_index = sw_if_index;
+ itp->itp_ai = ADJ_INDEX_INVALID;
itp->itp_n_sa_in = vec_len (sas_in);
for (ii = 0; ii < itp->itp_n_sa_in; ii++)
@@ -673,7 +692,24 @@ ipsec_tun_protect_update (u32 sw_if_index,
if (rv)
goto out;
- if (ip46_address_is_zero (&itp->itp_tun.dst))
+ if (ip46_address_is_zero (&itp->itp_tun.src))
+ {
+ /* must be one of thos pesky ipsec interfaces that has no encap.
+ * the encap then MUST comefrom the tunnel mode SA.
+ */
+ ipsec_sa_t *sa;
+
+ sa = ipsec_sa_get (itp->itp_out_sa);
+
+ if (!ipsec_sa_is_set_IS_TUNNEL (sa))
+ {
+ rv = VNET_API_ERROR_INVALID_DST_ADDRESS;
+ goto out;
+ }
+
+ itp->itp_flags |= IPSEC_PROTECT_ITF;
+ }
+ else if (ip46_address_is_zero (&itp->itp_tun.dst))
{
/* tunnel has no destination address, presumably because it's p2mp
in which case we use the nh that this is protection for */
@@ -690,7 +726,7 @@ ipsec_tun_protect_update (u32 sw_if_index,
/*
* add to the tunnel DB for ingress
- * - if the SA is in trasnport mode, then the packates will arrivw
+ * - if the SA is in trasnport mode, then the packates will arrive
* with the IP src,dst of the protected tunnel, in which case we can
* simply strip the IP header and hand the payload to the protocol
* appropriate input handler
@@ -752,6 +788,9 @@ ipsec_tun_protect_del (u32 sw_if_index, const ip_address_t * nh)
itp = ipsec_tun_protect_get (itpi);
ipsec_tun_protect_unconfig (im, itp);
+ if (ADJ_INDEX_INVALID != itp->itp_ai)
+ adj_unlock (itp->itp_ai);
+
clib_mem_free (itp->itp_key);
pool_put (ipsec_tun_protect_pool, itp);
@@ -828,13 +867,7 @@ ipsec_tun_protect_adj_delegate_adj_created (adj_index_t ai)
itpi = ipsec_tun_protect_find (adj->rewrite_header.sw_if_index, &ip);
if (INDEX_INVALID != itpi)
- {
- const ipsec_tun_protect_t *itp;
-
- itp = ipsec_tun_protect_get (itpi);
- adj_delegate_add (adj_get (ai), ipsec_tun_adj_delegate_type, itpi);
- ipsec_tun_protect_add_adj (ai, itp);
- }
+ ipsec_tun_protect_adj_add (ai, ipsec_tun_protect_get (itpi));
}
static u8 *
diff --git a/src/vnet/ipsec/ipsec_tun.h b/src/vnet/ipsec/ipsec_tun.h
index 90f299668dc..c5fbe59f2b6 100644
--- a/src/vnet/ipsec/ipsec_tun.h
+++ b/src/vnet/ipsec/ipsec_tun.h
@@ -47,12 +47,21 @@ typedef CLIB_PACKED(struct {
extern u8 *format_ipsec4_tunnel_key (u8 * s, va_list * args);
extern u8 *format_ipsec6_tunnel_key (u8 * s, va_list * args);
+#define foreach_ipsec_protect_flags \
+ _(L2, 1, "l2") \
+ _(ENCAPED, 2, "encapped") \
+ _(ITF, 4, "itf") \
+
typedef enum ipsec_protect_flags_t_
{
- IPSEC_PROTECT_L2 = (1 << 0),
- IPSEC_PROTECT_ENCAPED = (1 << 1),
+ IPSEC_PROTECT_NONE = 0,
+#define _(a,b,c) IPSEC_PROTECT_##a = b,
+ foreach_ipsec_protect_flags
+#undef _
} __clib_packed ipsec_protect_flags_t;
+extern u8 *format_ipsec_tun_protect_flags (u8 * s, va_list * args);
+
typedef struct ipsec_ep_t_
{
ip46_address_t src;
@@ -76,6 +85,7 @@ typedef struct ipsec_tun_protect_t_
ipsec_ep_t itp_crypto;
ipsec_protect_flags_t itp_flags;
+ adj_index_t itp_ai;
ipsec_ep_t itp_tun;
diff --git a/test/template_ipsec.py b/test/template_ipsec.py
index 1caed0dd932..c3484e1ad0b 100644
--- a/test/template_ipsec.py
+++ b/test/template_ipsec.py
@@ -914,6 +914,7 @@ class IpsecTun4(object):
def verify_tun_64(self, p, count=1):
self.vapi.cli("clear errors")
+ self.vapi.cli("clear ipsec sa")
try:
send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tun_sa, self.tun_if,
src=p.remote_tun_if_host6,
@@ -1104,6 +1105,7 @@ class IpsecTun6(object):
def verify_tun_46(self, p, count=1):
""" ipsec 4o6 tunnel basic test """
self.vapi.cli("clear errors")
+ self.vapi.cli("clear ipsec sa")
try:
send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if,
src=p.remote_tun_if_host4,
diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py
index a59baf1dfbf..183012608fe 100644
--- a/test/test_ipsec_tun_if_esp.py
+++ b/test/test_ipsec_tun_if_esp.py
@@ -15,7 +15,7 @@ from vpp_ipsec_tun_interface import VppIpsecTunInterface
from vpp_gre_interface import VppGreInterface
from vpp_ipip_tun_interface import VppIpIpTunInterface
from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
-from vpp_ipsec import VppIpsecSA, VppIpsecTunProtect
+from vpp_ipsec import VppIpsecSA, VppIpsecTunProtect, VppIpsecInterface
from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint
from vpp_teib import VppTeib
@@ -24,13 +24,18 @@ from vpp_papi import VppEnum
from vpp_acl import AclRule, VppAcl, VppAclInterface
-def config_tun_params(p, encryption_type, tun_if):
+def config_tun_params(p, encryption_type, tun_if, src=None, dst=None):
ip_class_by_addr_type = {socket.AF_INET: IP, socket.AF_INET6: IPv6}
esn_en = bool(p.flags & (VppEnum.vl_api_ipsec_sad_flags_t.
IPSEC_API_SAD_FLAG_USE_ESN))
crypt_key = mk_scapy_crypt_key(p)
- p.tun_dst = tun_if.remote_ip
- p.tun_src = tun_if.local_ip
+ if tun_if:
+ p.tun_dst = tun_if.remote_ip
+ p.tun_src = tun_if.local_ip
+ else:
+ p.tun_dst = dst
+ p.tun_src = src
+
p.scapy_tun_sa = SecurityAssociation(
encryption_type, spi=p.vpp_tun_spi,
crypt_algo=p.crypt_algo,
@@ -2473,5 +2478,298 @@ class TestIpsec6TunProtectTunDrop(TemplateIpsec,
self.unconfig_network(p)
+class TemplateIpsecItf4(object):
+ """ IPsec Interface IPv4 """
+
+ encryption_type = ESP
+ tun4_encrypt_node_name = "esp4-encrypt-tun"
+ tun4_decrypt_node_name = "esp4-decrypt-tun"
+ tun4_input_node = "ipsec4-tun-input"
+
+ def config_sa_tun(self, p, src, dst):
+ config_tun_params(p, self.encryption_type, None, src, dst)
+
+ p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+ p.auth_algo_vpp_id, p.auth_key,
+ p.crypt_algo_vpp_id, p.crypt_key,
+ self.vpp_esp_protocol,
+ src, dst,
+ flags=p.flags)
+ p.tun_sa_out.add_vpp_config()
+
+ p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+ p.auth_algo_vpp_id, p.auth_key,
+ p.crypt_algo_vpp_id, p.crypt_key,
+ self.vpp_esp_protocol,
+ dst, src,
+ flags=p.flags)
+ p.tun_sa_in.add_vpp_config()
+
+ def config_protect(self, p):
+ p.tun_protect = VppIpsecTunProtect(self,
+ p.tun_if,
+ p.tun_sa_out,
+ [p.tun_sa_in])
+ p.tun_protect.add_vpp_config()
+
+ def config_network(self, p):
+ p.tun_if = VppIpsecInterface(self)
+
+ p.tun_if.add_vpp_config()
+ p.tun_if.admin_up()
+ p.tun_if.config_ip4()
+ p.tun_if.config_ip6()
+
+ p.route = VppIpRoute(self, p.remote_tun_if_host, 32,
+ [VppRoutePath(p.tun_if.remote_ip4,
+ 0xffffffff)])
+ p.route.add_vpp_config()
+ r = VppIpRoute(self, p.remote_tun_if_host6, 128,
+ [VppRoutePath(p.tun_if.remote_ip6,
+ 0xffffffff,
+ proto=DpoProto.DPO_PROTO_IP6)])
+ r.add_vpp_config()
+
+ def unconfig_network(self, p):
+ p.route.remove_vpp_config()
+ p.tun_if.remove_vpp_config()
+
+ def unconfig_protect(self, p):
+ p.tun_protect.remove_vpp_config()
+
+ def unconfig_sa(self, p):
+ p.tun_sa_out.remove_vpp_config()
+ p.tun_sa_in.remove_vpp_config()
+
+
+class TestIpsecItf4(TemplateIpsec,
+ TemplateIpsecItf4,
+ IpsecTun4):
+ """ IPsec Interface IPv4 """
+
+ def setUp(self):
+ super(TestIpsecItf4, self).setUp()
+
+ self.tun_if = self.pg0
+
+ def tearDown(self):
+ super(TestIpsecItf4, self).tearDown()
+
+ def test_tun_44(self):
+ """IPSEC interface IPv4"""
+
+ n_pkts = 127
+ p = self.ipv4_params
+
+ self.config_network(p)
+ self.config_sa_tun(p,
+ self.pg0.local_ip4,
+ self.pg0.remote_ip4)
+ self.config_protect(p)
+
+ self.verify_tun_44(p, count=n_pkts)
+ c = p.tun_if.get_rx_stats()
+ self.assertEqual(c['packets'], n_pkts)
+ c = p.tun_if.get_tx_stats()
+ self.assertEqual(c['packets'], n_pkts)
+
+ p.tun_if.admin_down()
+ self.verify_tun_dropped_44(p, count=n_pkts)
+ p.tun_if.admin_up()
+ self.verify_tun_44(p, count=n_pkts)
+
+ c = p.tun_if.get_rx_stats()
+ self.assertEqual(c['packets'], 3*n_pkts)
+ c = p.tun_if.get_tx_stats()
+ self.assertEqual(c['packets'], 2*n_pkts)
+
+ # it's a v6 packet when its encrypted
+ self.tun4_encrypt_node_name = "esp6-encrypt-tun"
+
+ self.verify_tun_64(p, count=n_pkts)
+ c = p.tun_if.get_rx_stats()
+ self.assertEqual(c['packets'], 4*n_pkts)
+ c = p.tun_if.get_tx_stats()
+ self.assertEqual(c['packets'], 3*n_pkts)
+
+ self.tun4_encrypt_node_name = "esp4-encrypt-tun"
+
+ self.vapi.cli("clear interfaces")
+
+ # rekey - create new SAs and update the tunnel protection
+ np = copy.copy(p)
+ np.crypt_key = b'X' + p.crypt_key[1:]
+ np.scapy_tun_spi += 100
+ np.scapy_tun_sa_id += 1
+ np.vpp_tun_spi += 100
+ np.vpp_tun_sa_id += 1
+ np.tun_if.local_spi = p.vpp_tun_spi
+ np.tun_if.remote_spi = p.scapy_tun_spi
+
+ self.config_sa_tun(np,
+ self.pg0.local_ip4,
+ self.pg0.remote_ip4)
+ self.config_protect(np)
+ self.unconfig_sa(p)
+
+ self.verify_tun_44(np, count=n_pkts)
+ c = p.tun_if.get_rx_stats()
+ self.assertEqual(c['packets'], n_pkts)
+ c = p.tun_if.get_tx_stats()
+ self.assertEqual(c['packets'], n_pkts)
+
+ # teardown
+ self.unconfig_protect(np)
+ self.unconfig_sa(np)
+ self.unconfig_network(p)
+
+
+class TemplateIpsecItf6(object):
+ """ IPsec Interface IPv6 """
+
+ encryption_type = ESP
+ tun6_encrypt_node_name = "esp6-encrypt-tun"
+ tun6_decrypt_node_name = "esp6-decrypt-tun"
+ tun6_input_node = "ipsec6-tun-input"
+
+ def config_sa_tun(self, p, src, dst):
+ config_tun_params(p, self.encryption_type, None, src, dst)
+
+ p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+ p.auth_algo_vpp_id, p.auth_key,
+ p.crypt_algo_vpp_id, p.crypt_key,
+ self.vpp_esp_protocol,
+ src, dst,
+ flags=p.flags)
+ p.tun_sa_out.add_vpp_config()
+
+ p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+ p.auth_algo_vpp_id, p.auth_key,
+ p.crypt_algo_vpp_id, p.crypt_key,
+ self.vpp_esp_protocol,
+ dst, src,
+ flags=p.flags)
+ p.tun_sa_in.add_vpp_config()
+
+ def config_protect(self, p):
+ p.tun_protect = VppIpsecTunProtect(self,
+ p.tun_if,
+ p.tun_sa_out,
+ [p.tun_sa_in])
+ p.tun_protect.add_vpp_config()
+
+ def config_network(self, p):
+ p.tun_if = VppIpsecInterface(self)
+
+ p.tun_if.add_vpp_config()
+ p.tun_if.admin_up()
+ p.tun_if.config_ip4()
+ p.tun_if.config_ip6()
+
+ r = VppIpRoute(self, p.remote_tun_if_host4, 32,
+ [VppRoutePath(p.tun_if.remote_ip4,
+ 0xffffffff)])
+ r.add_vpp_config()
+
+ p.route = VppIpRoute(self, p.remote_tun_if_host, 128,
+ [VppRoutePath(p.tun_if.remote_ip6,
+ 0xffffffff,
+ proto=DpoProto.DPO_PROTO_IP6)])
+ p.route.add_vpp_config()
+
+ def unconfig_network(self, p):
+ p.route.remove_vpp_config()
+ p.tun_if.remove_vpp_config()
+
+ def unconfig_protect(self, p):
+ p.tun_protect.remove_vpp_config()
+
+ def unconfig_sa(self, p):
+ p.tun_sa_out.remove_vpp_config()
+ p.tun_sa_in.remove_vpp_config()
+
+
+class TestIpsecItf6(TemplateIpsec,
+ TemplateIpsecItf6,
+ IpsecTun6):
+ """ IPsec Interface IPv6 """
+
+ def setUp(self):
+ super(TestIpsecItf6, self).setUp()
+
+ self.tun_if = self.pg0
+
+ def tearDown(self):
+ super(TestIpsecItf6, self).tearDown()
+
+ def test_tun_44(self):
+ """IPSEC interface IPv6"""
+
+ n_pkts = 127
+ p = self.ipv6_params
+
+ self.config_network(p)
+ self.config_sa_tun(p,
+ self.pg0.local_ip6,
+ self.pg0.remote_ip6)
+ self.config_protect(p)
+
+ self.verify_tun_66(p, count=n_pkts)
+ c = p.tun_if.get_rx_stats()
+ self.assertEqual(c['packets'], n_pkts)
+ c = p.tun_if.get_tx_stats()
+ self.assertEqual(c['packets'], n_pkts)
+
+ p.tun_if.admin_down()
+ self.verify_drop_tun_66(p, count=n_pkts)
+ p.tun_if.admin_up()
+ self.verify_tun_66(p, count=n_pkts)
+
+ c = p.tun_if.get_rx_stats()
+ self.assertEqual(c['packets'], 3*n_pkts)
+ c = p.tun_if.get_tx_stats()
+ self.assertEqual(c['packets'], 2*n_pkts)
+
+ # it's a v4 packet when its encrypted
+ self.tun6_encrypt_node_name = "esp4-encrypt-tun"
+
+ self.verify_tun_46(p, count=n_pkts)
+ c = p.tun_if.get_rx_stats()
+ self.assertEqual(c['packets'], 4*n_pkts)
+ c = p.tun_if.get_tx_stats()
+ self.assertEqual(c['packets'], 3*n_pkts)
+
+ self.tun6_encrypt_node_name = "esp6-encrypt-tun"
+
+ self.vapi.cli("clear interfaces")
+
+ # rekey - create new SAs and update the tunnel protection
+ np = copy.copy(p)
+ np.crypt_key = b'X' + p.crypt_key[1:]
+ np.scapy_tun_spi += 100
+ np.scapy_tun_sa_id += 1
+ np.vpp_tun_spi += 100
+ np.vpp_tun_sa_id += 1
+ np.tun_if.local_spi = p.vpp_tun_spi
+ np.tun_if.remote_spi = p.scapy_tun_spi
+
+ self.config_sa_tun(np,
+ self.pg0.local_ip6,
+ self.pg0.remote_ip6)
+ self.config_protect(np)
+ self.unconfig_sa(p)
+
+ self.verify_tun_66(np, count=n_pkts)
+ c = p.tun_if.get_rx_stats()
+ self.assertEqual(c['packets'], n_pkts)
+ c = p.tun_if.get_tx_stats()
+ self.assertEqual(c['packets'], n_pkts)
+
+ # teardown
+ self.unconfig_protect(np)
+ self.unconfig_sa(np)
+ self.unconfig_network(p)
+
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_ipsec.py b/test/vpp_ipsec.py
index 985f6d4dc4e..f012a4a1e84 100644
--- a/test/vpp_ipsec.py
+++ b/test/vpp_ipsec.py
@@ -1,6 +1,7 @@
from vpp_object import VppObject
from ipaddress import ip_address
from vpp_papi import VppEnum
+from vpp_interface import VppInterface
try:
text_type = unicode
@@ -368,3 +369,41 @@ class VppIpsecTunProtect(VppObject):
self.nh == str(b.tun.nh):
return True
return False
+
+
+class VppIpsecInterface(VppInterface):
+ """
+ VPP IPSec interface
+ """
+
+ def __init__(self, test, mode=None):
+ super(VppIpsecInterface, self).__init__(test)
+
+ # only p2p mode is supported currently
+ self.mode = (VppEnum.vl_api_tunnel_mode_t.
+ TUNNEL_API_MODE_P2P)
+
+ def add_vpp_config(self):
+ r = self.test.vapi.ipsec_itf_create(itf={
+ 'user_instance': 0xffffffff,
+ 'mode': self.mode,
+ })
+ self.set_sw_if_index(r.sw_if_index)
+ self.test.registry.register(self, self.test.logger)
+ return self
+
+ def remove_vpp_config(self):
+ self.test.vapi.ipsec_itf_delete(sw_if_index=self._sw_if_index)
+
+ def query_vpp_config(self):
+ ts = self.test.vapi.ipsec_itf_dump(sw_if_index=0xffffffff)
+ for t in ts:
+ if t.tunnel.sw_if_index == self._sw_if_index:
+ return True
+ return False
+
+ def __str__(self):
+ return self.object_id()
+
+ def object_id(self):
+ return "ipsec-%d" % self._sw_if_index