diff options
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 * |