From dd4ccf2623b547654d215ffcf42f9813e42aa90c Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Tue, 30 Jun 2020 07:47:14 +0000 Subject: ipsec: Dedicated IPSec interface type Type: feature Signed-off-by: Neale Ranns Change-Id: Ie8bd50df163aea2798e9f9d35a13dcadc4a4a4b2 --- src/vnet/CMakeLists.txt | 1 + src/vnet/ipsec/ipsec.api | 51 ++++- src/vnet/ipsec/ipsec_api.c | 42 ++++ src/vnet/ipsec/ipsec_format.c | 20 +- src/vnet/ipsec/ipsec_itf.c | 462 ++++++++++++++++++++++++++++++++++++++++++ src/vnet/ipsec/ipsec_itf.h | 117 +++++++++++ src/vnet/ipsec/ipsec_tun.c | 61 ++++-- src/vnet/ipsec/ipsec_tun.h | 14 +- 8 files changed, 749 insertions(+), 19 deletions(-) create mode 100644 src/vnet/ipsec/ipsec_itf.c create mode 100644 src/vnet/ipsec/ipsec_itf.h (limited to 'src') 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 #include #include +#include #include #include @@ -33,6 +34,7 @@ #if WITH_LIBSSL > 0 #include #include +#include #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 +#include +#include +#include +#include + +/* 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 ]} + * 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 ]", + .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 } + * 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 ", + .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 +#include + +/** + * @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 +#include #include #include #include @@ -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; -- cgit 1.2.3-korg