X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Fipsec%2Fipsec_sa.c;h=fc8520d5ebbe5772b14a133cb34702af4910f9fe;hb=eba31ecebed1a7d168da17194cab7a8955761f2b;hp=a76197b9f50fe220570340cca84232360c853398;hpb=999c8ee6d6f1c07ba7877fb3f9aa66a90774aacc;p=vpp.git diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c index a76197b9f50..fc8520d5ebb 100644 --- a/src/vnet/ipsec/ipsec_sa.c +++ b/src/vnet/ipsec/ipsec_sa.c @@ -14,6 +14,17 @@ */ #include +#include + +/** + * @brief + * SA packet & bytes counters + */ +vlib_combined_counter_main_t ipsec_sa_counters = { + .name = "SA", + .stat_segment_name = "/net/ipsec/sa", +}; + static clib_error_t * ipsec_call_add_del_callbacks (ipsec_main_t * im, ipsec_sa_t * sa, @@ -37,8 +48,148 @@ 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 + */ +void +ipsec_sa_stack (ipsec_sa_t * sa) +{ + ipsec_main_t *im = &ipsec_main; + fib_forward_chain_type_t fct; + dpo_id_t tmp = DPO_INVALID; + + 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); + + dpo_stack_from_node ((sa->is_tunnel_ip6 ? + im->ah6_encrypt_node_index : + im->ah4_encrypt_node_index), + &sa->dpo[IPSEC_PROTOCOL_AH], &tmp); + dpo_stack_from_node ((sa->is_tunnel_ip6 ? + im->esp6_encrypt_node_index : + im->esp4_encrypt_node_index), + &sa->dpo[IPSEC_PROTOCOL_ESP], &tmp); + dpo_reset (&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; + + vlib_validate_combined_counter (&ipsec_sa_counters, sa_index); + vlib_zero_combined_counter (&ipsec_sa_counters, sa_index); + + sa->id = id; + sa->spi = spi; + sa->stat_index = sa_index; + 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 +197,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; } @@ -86,19 +231,16 @@ u8 ipsec_is_sa_used (u32 sa_index) { ipsec_main_t *im = &ipsec_main; - ipsec_spd_t *spd; - ipsec_policy_t *p; ipsec_tunnel_if_t *t; + ipsec_policy_t *p; /* *INDENT-OFF* */ - pool_foreach(spd, im->spds, ({ - pool_foreach(p, spd->policies, ({ - if (p->policy == IPSEC_POLICY_ACTION_PROTECT) - { - if (p->sa_index == sa_index) - return 1; - } - })); + pool_foreach(p, im->policies, ({ + if (p->policy == IPSEC_POLICY_ACTION_PROTECT) + { + if (p->sa_index == sa_index) + return 1; + } })); pool_foreach(t, im->tunnel_interfaces, ({ @@ -113,7 +255,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; @@ -121,7 +263,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 */ @@ -129,26 +271,25 @@ 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) - return VNET_API_ERROR_SYSCALL_ERROR_1; + { + clib_error_free (err); + return VNET_API_ERROR_SYSCALL_ERROR_1; + } } return 0; @@ -165,6 +306,90 @@ ipsec_get_sa_index_by_sa_id (u32 sa_id) return p[0]; } +void +ipsec_sa_walk (ipsec_sa_walk_cb_t cb, void *ctx) +{ + ipsec_main_t *im = &ipsec_main; + ipsec_sa_t *sa; + + /* *INDENT-OFF* */ + pool_foreach (sa, im->sad, + ({ + if (WALK_CONTINUE != cb(sa, ctx)) + break; + })); + /* *INDENT-ON* */ +} + +/** + * 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 *