diff options
author | Neale Ranns <nranns@cisco.com> | 2019-02-06 01:41:05 -0800 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2019-02-07 19:13:32 +0000 |
commit | 8d7c502002636da1cb7c71a87757f328e7c2c4fd (patch) | |
tree | 1005d63dcb3a24f7bb2ad2d3224bfcb062909666 /src/vnet/ipsec/ipsec_sa.c | |
parent | 3d0ef26a0285b9baa486c91b2e6609125a2bc651 (diff) |
IPSEC: no second lookup after tunnel encap
in the same maaner as with other tunnel tyeps we use
the FIB to cache and track the destination used to reach
the tunnel endpoint. Post encap we can then ship the packet
straight to this adjacency and thus elide the costly second
lookup.
- SA add and del function so they can be used both directly
from the API and for tunnels.
- API change for the SA dump to use the SA type
- ipsec_key_t type for convenience (copying, [un]formating)
- no matching tunnel counters in ipsec-if-input
Change-Id: I9d144a59667f7bf96442f4ca66bef5c1d3c7f1ea
Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'src/vnet/ipsec/ipsec_sa.c')
-rw-r--r-- | src/vnet/ipsec/ipsec_sa.c | 274 |
1 files changed, 236 insertions, 38 deletions
diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c index d439b4d46d3..f20d941fd68 100644 --- a/src/vnet/ipsec/ipsec_sa.c +++ b/src/vnet/ipsec/ipsec_sa.c @@ -14,6 +14,7 @@ */ #include <vnet/ipsec/ipsec.h> +#include <vnet/fib/fib_table.h> static clib_error_t * ipsec_call_add_del_callbacks (ipsec_main_t * im, ipsec_sa_t * sa, @@ -37,8 +38,146 @@ ipsec_call_add_del_callbacks (ipsec_main_t * im, ipsec_sa_t * sa, return 0; } +void +ipsec_mk_key (ipsec_key_t * key, const u8 * data, u8 len) +{ + memset (key, 0, sizeof (*key)); + + if (len > sizeof (key->data)) + key->len = sizeof (key->data); + else + key->len = len; + + memcpy (key->data, data, key->len); +} + +/** + * 'stack' (resolve the recursion for) the SA tunnel destination + */ +static void +ipsec_sa_stack (ipsec_sa_t * sa) +{ + fib_forward_chain_type_t fct; + dpo_id_t tmp = DPO_INVALID; + vlib_node_t *node; + + fct = fib_forw_chain_type_from_fib_proto ((sa->is_tunnel_ip6 ? + FIB_PROTOCOL_IP6 : + FIB_PROTOCOL_IP4)); + + fib_entry_contribute_forwarding (sa->fib_entry_index, fct, &tmp); + + node = vlib_get_node_by_name (vlib_get_main (), + (sa->is_tunnel_ip6 ? + (u8 *) "ah6-encrypt" : + (u8 *) "ah4-encrypt")); + dpo_stack_from_node (node->index, &sa->dpo[IPSEC_PROTOCOL_AH], &tmp); + + node = vlib_get_node_by_name (vlib_get_main (), + (sa->is_tunnel_ip6 ? + (u8 *) "esp6-encrypt" : + (u8 *) "esp4-encrypt")); + dpo_stack_from_node (node->index, &sa->dpo[IPSEC_PROTOCOL_ESP], &tmp); +} + int -ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add) +ipsec_sa_add (u32 id, + u32 spi, + ipsec_protocol_t proto, + ipsec_crypto_alg_t crypto_alg, + const ipsec_key_t * ck, + ipsec_integ_alg_t integ_alg, + const ipsec_key_t * ik, + ipsec_sa_flags_t flags, + u32 tx_table_id, + const ip46_address_t * tun_src, + const ip46_address_t * tun_dst, u32 * sa_out_index) +{ + ipsec_main_t *im = &ipsec_main; + clib_error_t *err; + ipsec_sa_t *sa; + u32 sa_index; + uword *p; + + p = hash_get (im->sa_index_by_sa_id, id); + if (p) + return VNET_API_ERROR_ENTRY_ALREADY_EXISTS; + + pool_get_zero (im->sad, sa); + + fib_node_init (&sa->node, FIB_NODE_TYPE_IPSEC_SA); + sa_index = sa - im->sad; + + sa->id = id; + sa->spi = spi; + sa->protocol = proto; + sa->crypto_alg = crypto_alg; + clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key)); + sa->integ_alg = integ_alg; + clib_memcpy (&sa->integ_key, ik, sizeof (sa->integ_key)); + ip46_address_copy (&sa->tunnel_src_addr, tun_src); + ip46_address_copy (&sa->tunnel_dst_addr, tun_dst); + + if (flags & IPSEC_SA_FLAG_USE_EXTENDED_SEQ_NUM) + sa->use_esn = 1; + if (flags & IPSEC_SA_FLAG_USE_ANTI_REPLAY) + sa->use_anti_replay = 1; + if (flags & IPSEC_SA_FLAG_IS_TUNNEL) + sa->is_tunnel = 1; + if (flags & IPSEC_SA_FLAG_IS_TUNNEL_V6) + sa->is_tunnel_ip6 = 1; + if (flags & IPSEC_SA_FLAG_UDP_ENCAP) + sa->udp_encap = 1; + + err = ipsec_check_support_cb (im, sa); + if (err) + { + clib_warning ("%s", err->what); + pool_put (im->sad, sa); + return VNET_API_ERROR_UNIMPLEMENTED; + } + + err = ipsec_call_add_del_callbacks (im, sa, sa_index, 1); + if (err) + { + pool_put (im->sad, sa); + return VNET_API_ERROR_SYSCALL_ERROR_1; + } + + if (sa->is_tunnel) + { + fib_protocol_t fproto = (sa->is_tunnel_ip6 ? + FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4); + fib_prefix_t pfx = { + .fp_addr = sa->tunnel_dst_addr, + .fp_len = (sa->is_tunnel_ip6 ? 128 : 32), + .fp_proto = fproto, + }; + sa->tx_fib_index = fib_table_find (fproto, tx_table_id); + if (sa->tx_fib_index == ~((u32) 0)) + { + pool_put (im->sad, sa); + return VNET_API_ERROR_NO_SUCH_FIB; + } + + sa->fib_entry_index = fib_table_entry_special_add (sa->tx_fib_index, + &pfx, + FIB_SOURCE_RR, + FIB_ENTRY_FLAG_NONE); + sa->sibling = fib_entry_child_add (sa->fib_entry_index, + FIB_NODE_TYPE_IPSEC_SA, sa_index); + ipsec_sa_stack (sa); + } + hash_set (im->sa_index_by_sa_id, sa->id, sa_index); + + if (sa_out_index) + *sa_out_index = sa_index; + + return (0); +} + +u32 +ipsec_sa_del (u32 id) { ipsec_main_t *im = &ipsec_main; ipsec_sa_t *sa = 0; @@ -46,39 +185,33 @@ ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add) u32 sa_index; clib_error_t *err; - clib_warning ("id %u spi %u", new_sa->id, new_sa->spi); + p = hash_get (im->sa_index_by_sa_id, id); - p = hash_get (im->sa_index_by_sa_id, new_sa->id); - if (p && is_add) - return VNET_API_ERROR_ENTRY_ALREADY_EXISTS; - if (!p && !is_add) + if (!p) return VNET_API_ERROR_NO_SUCH_ENTRY; - if (!is_add) /* delete */ + sa_index = p[0]; + sa = pool_elt_at_index (im->sad, sa_index); + if (ipsec_is_sa_used (sa_index)) { - sa_index = p[0]; - sa = pool_elt_at_index (im->sad, sa_index); - if (ipsec_is_sa_used (sa_index)) - { - clib_warning ("sa_id %u used in policy", sa->id); - return VNET_API_ERROR_SYSCALL_ERROR_1; /* sa used in policy */ - } - hash_unset (im->sa_index_by_sa_id, sa->id); - err = ipsec_call_add_del_callbacks (im, sa, sa_index, 0); - if (err) - return VNET_API_ERROR_SYSCALL_ERROR_1; - pool_put (im->sad, sa); + clib_warning ("sa_id %u used in policy", sa->id); + /* sa used in policy */ + return VNET_API_ERROR_SYSCALL_ERROR_1; } - else /* create new SA */ + hash_unset (im->sa_index_by_sa_id, sa->id); + err = ipsec_call_add_del_callbacks (im, sa, sa_index, 0); + if (err) + return VNET_API_ERROR_SYSCALL_ERROR_1; + if (sa->is_tunnel) { - pool_get (im->sad, sa); - clib_memcpy (sa, new_sa, sizeof (*sa)); - sa_index = sa - im->sad; - hash_set (im->sa_index_by_sa_id, sa->id, sa_index); - err = ipsec_call_add_del_callbacks (im, sa, sa_index, 1); - if (err) - return VNET_API_ERROR_SYSCALL_ERROR_1; + fib_entry_child_remove (sa->fib_entry_index, sa->sibling); + fib_table_entry_special_remove + (sa->tx_fib_index, + fib_entry_get_prefix (sa->fib_entry_index), FIB_SOURCE_RR); + dpo_reset (&sa->dpo[IPSEC_PROTOCOL_AH]); + dpo_reset (&sa->dpo[IPSEC_PROTOCOL_ESP]); } + pool_put (im->sad, sa); return 0; } @@ -110,7 +243,7 @@ ipsec_is_sa_used (u32 sa_index) } int -ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update) +ipsec_set_sa_key (u32 id, const ipsec_key_t * ck, const ipsec_key_t * ik) { ipsec_main_t *im = &ipsec_main; uword *p; @@ -118,7 +251,7 @@ ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update) ipsec_sa_t *sa = 0; clib_error_t *err; - p = hash_get (im->sa_index_by_sa_id, sa_update->id); + p = hash_get (im->sa_index_by_sa_id, id); if (!p) return VNET_API_ERROR_SYSCALL_ERROR_1; /* no such sa-id */ @@ -126,22 +259,18 @@ ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update) sa = pool_elt_at_index (im->sad, sa_index); /* new crypto key */ - if (0 < sa_update->crypto_key_len) + if (ck) { - clib_memcpy (sa->crypto_key, sa_update->crypto_key, - sa_update->crypto_key_len); - sa->crypto_key_len = sa_update->crypto_key_len; + clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key)); } /* new integ key */ - if (0 < sa_update->integ_key_len) + if (ik) { - clib_memcpy (sa->integ_key, sa_update->integ_key, - sa_update->integ_key_len); - sa->integ_key_len = sa_update->integ_key_len; + clib_memcpy (&sa->integ_key, 0, sizeof (sa->integ_key)); } - if (0 < sa_update->crypto_key_len || 0 < sa_update->integ_key_len) + if (ck || ik) { err = ipsec_call_add_del_callbacks (im, sa, sa_index, 0); if (err) @@ -162,6 +291,75 @@ ipsec_get_sa_index_by_sa_id (u32 sa_id) return p[0]; } +/** + * Function definition to get a FIB node from its index + */ +static fib_node_t * +ipsec_sa_fib_node_get (fib_node_index_t index) +{ + ipsec_main_t *im; + ipsec_sa_t *sa; + + im = &ipsec_main; + sa = pool_elt_at_index (im->sad, index); + + return (&sa->node); +} + +/** + * Function definition to inform the FIB node that its last lock has gone. + */ +static void +ipsec_sa_last_lock_gone (fib_node_t * node) +{ + /* + * The ipsec SA is a root of the graph. As such + * it never has children and thus is never locked. + */ + ASSERT (0); +} + +static ipsec_sa_t * +ipsec_sa_from_fib_node (fib_node_t * node) +{ + ASSERT (FIB_NODE_TYPE_IPSEC_SA == node->fn_type); + return ((ipsec_sa_t *) (((char *) node) - + STRUCT_OFFSET_OF (ipsec_sa_t, node))); + +} + +/** + * Function definition to backwalk a FIB node + */ +static fib_node_back_walk_rc_t +ipsec_sa_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx) +{ + ipsec_sa_stack (ipsec_sa_from_fib_node (node)); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/* + * Virtual function table registered by MPLS GRE tunnels + * for participation in the FIB object graph. + */ +const static fib_node_vft_t ipsec_sa_vft = { + .fnv_get = ipsec_sa_fib_node_get, + .fnv_last_lock = ipsec_sa_last_lock_gone, + .fnv_back_walk = ipsec_sa_back_walk, +}; + +/* force inclusion from application's main.c */ +clib_error_t * +ipsec_sa_interface_init (vlib_main_t * vm) +{ + fib_node_register_type (FIB_NODE_TYPE_IPSEC_SA, &ipsec_sa_vft); + + return 0; +} + +VLIB_INIT_FUNCTION (ipsec_sa_interface_init); + /* * fd.io coding-style-patch-verification: ON * |