ipsec: manually binding an SA to a worker
An SA is normally bound to the first thread using it. However, one could want to manually bind an SA to a specific worker. Type: improvement Signed-off-by: Maxime Peim <mpeim@cisco.com> Change-Id: I05cbbf753e44a01d9964ee47812c964db9bbb488
This commit is contained in:
@ -201,6 +201,29 @@ autoreply define ipsec_sad_entry_del
|
|||||||
u32 id;
|
u32 id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief An API to bind an SAD entry to a specific worker
|
||||||
|
|
||||||
|
@param client_index - opaque cookie to identify the sender
|
||||||
|
@param context - sender context, to match reply w/ request
|
||||||
|
@param sa_id - the id of the SA to bind
|
||||||
|
@param worker - the worker's index to which the SA will be bound to
|
||||||
|
*/
|
||||||
|
autoreply define ipsec_sad_bind
|
||||||
|
{
|
||||||
|
u32 client_index;
|
||||||
|
u32 context;
|
||||||
|
u32 sa_id;
|
||||||
|
u32 worker;
|
||||||
|
};
|
||||||
|
|
||||||
|
autoreply define ipsec_sad_unbind
|
||||||
|
{
|
||||||
|
u32 client_index;
|
||||||
|
u32 context;
|
||||||
|
u32 sa_id;
|
||||||
|
};
|
||||||
|
|
||||||
/** \brief An API to update the tunnel parameters and the ports associated with an SA
|
/** \brief An API to update the tunnel parameters and the ports associated with an SA
|
||||||
|
|
||||||
Used in the NAT-T case when the NAT data changes
|
Used in the NAT-T case when the NAT data changes
|
||||||
@ -430,6 +453,12 @@ define ipsec_sa_v3_dump
|
|||||||
u32 context;
|
u32 context;
|
||||||
u32 sa_id;
|
u32 sa_id;
|
||||||
};
|
};
|
||||||
|
define ipsec_sa_v4_dump
|
||||||
|
{
|
||||||
|
u32 client_index;
|
||||||
|
u32 context;
|
||||||
|
u32 sa_id;
|
||||||
|
};
|
||||||
|
|
||||||
/** \brief IPsec security association database response
|
/** \brief IPsec security association database response
|
||||||
@param context - sender context which was passed in the request
|
@param context - sender context which was passed in the request
|
||||||
@ -479,6 +508,18 @@ define ipsec_sa_v3_details {
|
|||||||
|
|
||||||
u32 stat_index;
|
u32 stat_index;
|
||||||
};
|
};
|
||||||
|
define ipsec_sa_v4_details {
|
||||||
|
u32 context;
|
||||||
|
vl_api_ipsec_sad_entry_v3_t entry;
|
||||||
|
|
||||||
|
vl_api_interface_index_t sw_if_index;
|
||||||
|
u64 seq_outbound;
|
||||||
|
u64 last_seq_inbound;
|
||||||
|
u64 replay_window;
|
||||||
|
|
||||||
|
u32 thread_index;
|
||||||
|
u32 stat_index;
|
||||||
|
};
|
||||||
|
|
||||||
/** \brief Dump IPsec backends
|
/** \brief Dump IPsec backends
|
||||||
@param client_index - opaque cookie to identify the sender
|
@param client_index - opaque cookie to identify the sender
|
||||||
|
@ -592,6 +592,36 @@ out:
|
|||||||
REPLY_MACRO (VL_API_IPSEC_SAD_ENTRY_UPDATE_REPLY);
|
REPLY_MACRO (VL_API_IPSEC_SAD_ENTRY_UPDATE_REPLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vl_api_ipsec_sad_bind_t_handler (vl_api_ipsec_sad_bind_t *mp)
|
||||||
|
{
|
||||||
|
vl_api_ipsec_sad_bind_reply_t *rmp;
|
||||||
|
u32 sa_id;
|
||||||
|
u32 worker;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
sa_id = ntohl (mp->sa_id);
|
||||||
|
worker = ntohl (mp->worker);
|
||||||
|
|
||||||
|
rv = ipsec_sa_bind (sa_id, worker, true /* bind */);
|
||||||
|
|
||||||
|
REPLY_MACRO (VL_API_IPSEC_SAD_BIND_REPLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vl_api_ipsec_sad_unbind_t_handler (vl_api_ipsec_sad_unbind_t *mp)
|
||||||
|
{
|
||||||
|
vl_api_ipsec_sad_unbind_reply_t *rmp;
|
||||||
|
u32 sa_id;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
sa_id = ntohl (mp->sa_id);
|
||||||
|
|
||||||
|
rv = ipsec_sa_bind (sa_id, ~0, false /* bind */);
|
||||||
|
|
||||||
|
REPLY_MACRO (VL_API_IPSEC_SAD_UNBIND_REPLY);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_ipsec_spds_details (ipsec_spd_t * spd, vl_api_registration_t * reg,
|
send_ipsec_spds_details (ipsec_spd_t * spd, vl_api_registration_t * reg,
|
||||||
u32 context)
|
u32 context)
|
||||||
@ -1116,6 +1146,87 @@ vl_api_ipsec_sa_v3_dump_t_handler (vl_api_ipsec_sa_v3_dump_t *mp)
|
|||||||
ipsec_sa_walk (send_ipsec_sa_v3_details, &ctx);
|
ipsec_sa_walk (send_ipsec_sa_v3_details, &ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static walk_rc_t
|
||||||
|
send_ipsec_sa_v4_details (ipsec_sa_t *sa, void *arg)
|
||||||
|
{
|
||||||
|
ipsec_dump_walk_ctx_t *ctx = arg;
|
||||||
|
vl_api_ipsec_sa_v4_details_t *mp;
|
||||||
|
|
||||||
|
mp = vl_msg_api_alloc (sizeof (*mp));
|
||||||
|
clib_memset (mp, 0, sizeof (*mp));
|
||||||
|
mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_SA_V4_DETAILS);
|
||||||
|
mp->context = ctx->context;
|
||||||
|
|
||||||
|
mp->entry.sad_id = htonl (sa->id);
|
||||||
|
mp->entry.spi = htonl (sa->spi);
|
||||||
|
mp->entry.protocol = ipsec_proto_encode (sa->protocol);
|
||||||
|
|
||||||
|
mp->entry.crypto_algorithm = ipsec_crypto_algo_encode (sa->crypto_alg);
|
||||||
|
ipsec_key_encode (&sa->crypto_key, &mp->entry.crypto_key);
|
||||||
|
|
||||||
|
mp->entry.integrity_algorithm = ipsec_integ_algo_encode (sa->integ_alg);
|
||||||
|
ipsec_key_encode (&sa->integ_key, &mp->entry.integrity_key);
|
||||||
|
|
||||||
|
mp->entry.flags = ipsec_sad_flags_encode (sa);
|
||||||
|
mp->entry.salt = clib_host_to_net_u32 (sa->salt);
|
||||||
|
|
||||||
|
if (ipsec_sa_is_set_IS_PROTECT (sa))
|
||||||
|
{
|
||||||
|
ipsec_sa_dump_match_ctx_t ctx = {
|
||||||
|
.sai = sa - ipsec_sa_pool,
|
||||||
|
.sw_if_index = ~0,
|
||||||
|
};
|
||||||
|
ipsec_tun_protect_walk (ipsec_sa_dump_match_sa, &ctx);
|
||||||
|
|
||||||
|
mp->sw_if_index = htonl (ctx.sw_if_index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mp->sw_if_index = ~0;
|
||||||
|
|
||||||
|
if (ipsec_sa_is_set_IS_TUNNEL (sa))
|
||||||
|
tunnel_encode (&sa->tunnel, &mp->entry.tunnel);
|
||||||
|
|
||||||
|
if (ipsec_sa_is_set_UDP_ENCAP (sa))
|
||||||
|
{
|
||||||
|
mp->entry.udp_src_port = sa->udp_hdr.src_port;
|
||||||
|
mp->entry.udp_dst_port = sa->udp_hdr.dst_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp->seq_outbound = clib_host_to_net_u64 (((u64) sa->seq));
|
||||||
|
mp->last_seq_inbound = clib_host_to_net_u64 (((u64) sa->seq));
|
||||||
|
if (ipsec_sa_is_set_USE_ESN (sa))
|
||||||
|
{
|
||||||
|
mp->seq_outbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
|
||||||
|
mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
|
||||||
|
}
|
||||||
|
if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
|
||||||
|
mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
|
||||||
|
|
||||||
|
mp->thread_index = clib_host_to_net_u32 (sa->thread_index);
|
||||||
|
mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
|
||||||
|
|
||||||
|
vl_api_send_msg (ctx->reg, (u8 *) mp);
|
||||||
|
|
||||||
|
return (WALK_CONTINUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vl_api_ipsec_sa_v4_dump_t_handler (vl_api_ipsec_sa_v4_dump_t *mp)
|
||||||
|
{
|
||||||
|
vl_api_registration_t *reg;
|
||||||
|
|
||||||
|
reg = vl_api_client_index_to_registration (mp->client_index);
|
||||||
|
if (!reg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ipsec_dump_walk_ctx_t ctx = {
|
||||||
|
.reg = reg,
|
||||||
|
.context = mp->context,
|
||||||
|
};
|
||||||
|
|
||||||
|
ipsec_sa_walk (send_ipsec_sa_v4_details, &ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vl_api_ipsec_backend_dump_t_handler (vl_api_ipsec_backend_dump_t * mp)
|
vl_api_ipsec_backend_dump_t_handler (vl_api_ipsec_backend_dump_t * mp)
|
||||||
{
|
{
|
||||||
|
@ -211,6 +211,71 @@ VLIB_CLI_COMMAND (ipsec_sa_add_del_command, static) = {
|
|||||||
};
|
};
|
||||||
/* *INDENT-ON* */
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
static clib_error_t *
|
||||||
|
ipsec_sa_bind_cli (vlib_main_t *vm, unformat_input_t *input,
|
||||||
|
vlib_cli_command_t *cmd)
|
||||||
|
{
|
||||||
|
unformat_input_t _line_input, *line_input = &_line_input;
|
||||||
|
u32 id = ~0;
|
||||||
|
u32 worker = ~0;
|
||||||
|
bool bind = 1;
|
||||||
|
int rv;
|
||||||
|
clib_error_t *error = NULL;
|
||||||
|
|
||||||
|
if (!unformat_user (input, unformat_line_input, line_input))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
|
||||||
|
{
|
||||||
|
if (unformat (line_input, "unbind"))
|
||||||
|
bind = 0;
|
||||||
|
else if (id != ~0 && unformat (line_input, "%u", &id))
|
||||||
|
;
|
||||||
|
else if (unformat (line_input, "%u", &worker))
|
||||||
|
;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = clib_error_return (0, "parse error: '%U'",
|
||||||
|
format_unformat_error, line_input);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == ~0)
|
||||||
|
{
|
||||||
|
error = clib_error_return (0, "please specify SA ID");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind && ~0 == worker)
|
||||||
|
{
|
||||||
|
error = clib_error_return (0, "please specify worker to bind to");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = ipsec_sa_bind (id, worker, bind);
|
||||||
|
switch (rv)
|
||||||
|
{
|
||||||
|
case VNET_API_ERROR_INVALID_VALUE:
|
||||||
|
error = clib_error_return (0, "please specify a valid SA ID");
|
||||||
|
break;
|
||||||
|
case VNET_API_ERROR_INVALID_WORKER:
|
||||||
|
error = clib_error_return (0, "please specify a valid worker index");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
unformat_free (line_input);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
VLIB_CLI_COMMAND (ipsec_sa_bind_cmd, static) = {
|
||||||
|
.path = "ipsec sa bind",
|
||||||
|
.short_help = "ipsec sa [unbind] <sa-id> <worker>",
|
||||||
|
.function = ipsec_sa_bind_cli,
|
||||||
|
};
|
||||||
|
|
||||||
static clib_error_t *
|
static clib_error_t *
|
||||||
ipsec_spd_add_del_command_fn (vlib_main_t * vm,
|
ipsec_spd_add_del_command_fn (vlib_main_t * vm,
|
||||||
unformat_input_t * input,
|
unformat_input_t * input,
|
||||||
|
@ -521,6 +521,32 @@ ipsec_sa_del (ipsec_sa_t * sa)
|
|||||||
pool_put (ipsec_sa_pool, sa);
|
pool_put (ipsec_sa_pool, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ipsec_sa_bind (u32 id, u32 worker, bool bind)
|
||||||
|
{
|
||||||
|
ipsec_main_t *im = &ipsec_main;
|
||||||
|
uword *p;
|
||||||
|
ipsec_sa_t *sa;
|
||||||
|
|
||||||
|
p = hash_get (im->sa_index_by_sa_id, id);
|
||||||
|
if (!p)
|
||||||
|
return VNET_API_ERROR_INVALID_VALUE;
|
||||||
|
|
||||||
|
sa = ipsec_sa_get (p[0]);
|
||||||
|
|
||||||
|
if (!bind)
|
||||||
|
{
|
||||||
|
sa->thread_index = ~0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (worker >= vlib_num_workers ())
|
||||||
|
return VNET_API_ERROR_INVALID_WORKER;
|
||||||
|
|
||||||
|
sa->thread_index = vlib_get_worker_thread_index (worker);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ipsec_sa_unlock (index_t sai)
|
ipsec_sa_unlock (index_t sai)
|
||||||
{
|
{
|
||||||
@ -618,19 +644,18 @@ ipsec_sa_fib_node_get (fib_node_index_t index)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ipsec_sa_t *
|
static ipsec_sa_t *
|
||||||
ipsec_sa_from_fib_node (fib_node_t * node)
|
ipsec_sa_from_fib_node (fib_node_t *node)
|
||||||
{
|
{
|
||||||
ASSERT (FIB_NODE_TYPE_IPSEC_SA == node->fn_type);
|
ASSERT (FIB_NODE_TYPE_IPSEC_SA == node->fn_type);
|
||||||
return ((ipsec_sa_t *) (((char *) node) -
|
return (
|
||||||
STRUCT_OFFSET_OF (ipsec_sa_t, node)));
|
(ipsec_sa_t *) (((char *) node) - STRUCT_OFFSET_OF (ipsec_sa_t, node)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function definition to inform the FIB node that its last lock has gone.
|
* Function definition to inform the FIB node that its last lock has gone.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ipsec_sa_last_lock_gone (fib_node_t * node)
|
ipsec_sa_last_lock_gone (fib_node_t *node)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The ipsec SA is a root of the graph. As such
|
* The ipsec SA is a root of the graph. As such
|
||||||
@ -643,7 +668,7 @@ ipsec_sa_last_lock_gone (fib_node_t * node)
|
|||||||
* Function definition to backwalk a FIB node
|
* Function definition to backwalk a FIB node
|
||||||
*/
|
*/
|
||||||
static fib_node_back_walk_rc_t
|
static fib_node_back_walk_rc_t
|
||||||
ipsec_sa_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
|
ipsec_sa_back_walk (fib_node_t *node, fib_node_back_walk_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
ipsec_sa_stack (ipsec_sa_from_fib_node (node));
|
ipsec_sa_stack (ipsec_sa_from_fib_node (node));
|
||||||
|
|
||||||
|
@ -283,6 +283,7 @@ ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
|
|||||||
ipsec_integ_alg_t integ_alg, const ipsec_key_t *ik,
|
ipsec_integ_alg_t integ_alg, const ipsec_key_t *ik,
|
||||||
ipsec_sa_flags_t flags, u32 salt, u16 src_port,
|
ipsec_sa_flags_t flags, u32 salt, u16 src_port,
|
||||||
u16 dst_port, const tunnel_t *tun, u32 *sa_out_index);
|
u16 dst_port, const tunnel_t *tun, u32 *sa_out_index);
|
||||||
|
extern int ipsec_sa_bind (u32 id, u32 worker, bool bind);
|
||||||
extern index_t ipsec_sa_find_and_lock (u32 id);
|
extern index_t ipsec_sa_find_and_lock (u32 id);
|
||||||
extern int ipsec_sa_unlock_id (u32 id);
|
extern int ipsec_sa_unlock_id (u32 id);
|
||||||
extern void ipsec_sa_unlock (index_t sai);
|
extern void ipsec_sa_unlock (index_t sai);
|
||||||
|
@ -288,6 +288,18 @@ api_ipsec_sad_entry_del (vat_main_t *vat)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
api_ipsec_sad_bind (vat_main_t *vat)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
api_ipsec_sad_unbind (vat_main_t *vat)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vl_api_ipsec_sad_entry_add_del_v2_reply_t_handler (
|
vl_api_ipsec_sad_entry_add_del_v2_reply_t_handler (
|
||||||
vl_api_ipsec_sad_entry_add_del_v2_reply_t *mp)
|
vl_api_ipsec_sad_entry_add_del_v2_reply_t *mp)
|
||||||
@ -433,6 +445,17 @@ api_ipsec_sa_dump (vat_main_t *vam)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vl_api_ipsec_sa_v4_details_t_handler (vl_api_ipsec_sa_v4_details_t *mp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
api_ipsec_sa_v4_dump (vat_main_t *mp)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vl_api_ipsec_sa_details_t_handler (vl_api_ipsec_sa_details_t *mp)
|
vl_api_ipsec_sa_details_t_handler (vl_api_ipsec_sa_details_t *mp)
|
||||||
{
|
{
|
||||||
|
@ -4,10 +4,14 @@ from framework import VppTestCase, VppTestRunner
|
|||||||
from template_ipsec import TemplateIpsec, IPsecIPv4Params
|
from template_ipsec import TemplateIpsec, IPsecIPv4Params
|
||||||
from vpp_papi import VppEnum
|
from vpp_papi import VppEnum
|
||||||
|
|
||||||
|
from vpp_ipsec import VppIpsecSA
|
||||||
|
|
||||||
|
|
||||||
class IpsecApiTestCase(VppTestCase):
|
class IpsecApiTestCase(VppTestCase):
|
||||||
"""IPSec API tests"""
|
"""IPSec API tests"""
|
||||||
|
|
||||||
|
vpp_worker_count = 2
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super(IpsecApiTestCase, cls).setUpClass()
|
super(IpsecApiTestCase, cls).setUpClass()
|
||||||
@ -115,6 +119,40 @@ class IpsecApiTestCase(VppTestCase):
|
|||||||
)
|
)
|
||||||
self.vapi.ipsec_select_backend(protocol=self.vpp_ah_protocol, index=0)
|
self.vapi.ipsec_select_backend(protocol=self.vpp_ah_protocol, index=0)
|
||||||
|
|
||||||
|
def __check_sa_binding(self, sa_id, thread_index):
|
||||||
|
found_sa = False
|
||||||
|
sa_dumps = self.vapi.ipsec_sa_v4_dump()
|
||||||
|
for dump in sa_dumps:
|
||||||
|
if dump.entry.sad_id == sa_id:
|
||||||
|
self.assertEqual(dump.thread_index, thread_index)
|
||||||
|
found_sa = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found_sa:
|
||||||
|
self.fail("SA not found in VPP")
|
||||||
|
|
||||||
|
def test_sa_worker_bind(self):
|
||||||
|
"""Bind an SA to a worker"""
|
||||||
|
sa = VppIpsecSA(
|
||||||
|
self,
|
||||||
|
self.ipv4_params.scapy_tun_sa_id,
|
||||||
|
self.ipv4_params.scapy_tun_spi,
|
||||||
|
self.ipv4_params.auth_algo_vpp_id,
|
||||||
|
self.ipv4_params.auth_key,
|
||||||
|
self.ipv4_params.crypt_algo_vpp_id,
|
||||||
|
self.ipv4_params.crypt_key,
|
||||||
|
VppEnum.vl_api_ipsec_proto_t.IPSEC_API_PROTO_ESP,
|
||||||
|
)
|
||||||
|
sa.add_vpp_config()
|
||||||
|
|
||||||
|
self.__check_sa_binding(sa.id, 0xFFFF)
|
||||||
|
|
||||||
|
self.vapi.ipsec_sad_bind(sa_id=sa.id, worker=1)
|
||||||
|
|
||||||
|
self.__check_sa_binding(sa.id, 2)
|
||||||
|
|
||||||
|
sa.remove_vpp_config()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main(testRunner=VppTestRunner)
|
unittest.main(testRunner=VppTestRunner)
|
||||||
|
Reference in New Issue
Block a user