ipsec: Dedicated IPSec interface type
Type: feature
Signed-off-by: Neale Ranns <nranns@cisco.com>
Change-Id: Ie8bd50df163aea2798e9f9d35a13dcadc4a4a4b2
diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api
index dee9144..488df1c 100644
--- a/src/vnet/ipsec/ipsec.api
+++ b/src/vnet/ipsec/ipsec.api
@@ -20,6 +20,7 @@
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 @@
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 ef58f7a..667d9b2 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_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 @@
/* *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 98de779..e3c6f22 100644
--- a/src/vnet/ipsec/ipsec_format.c
+++ b/src/vnet/ipsec/ipsec_format.c
@@ -353,6 +353,21 @@
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 @@
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 0000000..756bc19
--- /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 0000000..93e03f7
--- /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 9b76d31..fb530c8 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 @@
}
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_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 @@
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 @@
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 @@
{
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 @@
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 @@
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 @@
/*
* 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 @@
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 @@
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 90f2996..c5fbe59 100644
--- a/src/vnet/ipsec/ipsec_tun.h
+++ b/src/vnet/ipsec/ipsec_tun.h
@@ -47,12 +47,21 @@
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 @@
ipsec_ep_t itp_crypto;
ipsec_protect_flags_t itp_flags;
+ adj_index_t itp_ai;
ipsec_ep_t itp_tun;