summaryrefslogtreecommitdiffstats
path: root/src/vnet/session/session_api.c
AgeCommit message (Collapse)AuthorFilesLines
2019-08-28session: fix unbind coverity warningFlorin Coras1-0/+7
Type:fix Change-Id: If4fce6bd9b06ed545d72df847afd6a74cdefe042 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-08-27session: move ctrl messages from bapi to mqFlorin Coras1-26/+138
Type:refactor Moves connect, disconnect, bind, unbind and app detach to message queue from binary api. Simplifies app/vcl interaction with the session layer since all session control messages are now handled over the mq. Add/del segment messages require internal C api changes which affect all builtin applications. They'll be moved in a different patch and might not be back portable to 19.08. Change-Id: I93f6d18e551b024effa75d47f5ff25f23ba8aff5 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-08-01session: session pool migration notificationFlorin Coras1-0/+8
Type: feature Change-Id: Ia9a5b1a6d85c7f3f2e6db583b33b5b20029a1e88 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-07-19session: Use parent_handle instead of transport_optsNathan Skrzypczak1-1/+1
Type: feature This is mostly used for quic in the case of a stream creation (i.e. connect on an already established QUIC session). We want do default parent_handle to INVALID to be able to distinguish it from parent_handle = 0 Change-Id: Id5ac0b0155a3c44e51334231b711e4fd87a96a10 Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
2019-07-08session: add flag to disable session lookupNathan Skrzypczak1-0/+1
Type: feature Change-Id: I1369859be0a722ea37e5d3ecb35dee5684fc69f8 Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
2019-06-17session: use listener_handle instead of listener_indexNathan Skrzypczak1-2/+2
Type: refactor Change-Id: I97fa59a0ba0b6b7a98698926020ffffcf6ae6ba3 Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
2019-06-12session: use session handles as app listener handlesFlorin Coras1-5/+2
Type: refactor Change-Id: I421750147a8a821bd0b522daf6c2b7239e551f12 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-05-16Add transport_opts to connect_sock bapiNathan Skrzypczak1-1/+2
Needed by QUIC to distinguish Q/Ssessions Change-Id: Idcc9e46f86f54a7d06ce6d870edec1766e95c82d Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
2019-05-07add TLS endpoint functionYu Ping1-5/+6
a bit like QUIC, and the added function can get the endpoint info in TLS, so that the VCL layer can get those info correctly Change-Id: Ied7aa3077087c1814499364dfa7654a088ad9910 Signed-off-by: Yu Ping <ping.yu@intel.com>
2019-04-25session: cleanup segment manager and fifo segmentFlorin Coras1-1/+1
Change-Id: I984f347fb465c0c405cef668d8690457e81788e2 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-04-25session: use teps in accept/connect notificationsFlorin Coras1-8/+6
Change-Id: I58e713661a38cecbfdebd4609292d9d12e880cd2 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-04-24Add get_endpoint in transport vftAloys Augustin1-10/+7
This allows QUIC & TLS specific logic to be implemented, and meaningfull IP/port to be returned when connection is overridden. Change-Id: Id79c59fe4d7b16d36f0e96ad3e281c4026b5fe65 Signed-off-by: Aloys Augustin <aloaugus@cisco.com>
2019-04-08fixing typosJim Thompson1-1/+1
Change-Id: I215e1e0208a073db80ec6f87695d734cf40fabe3 Signed-off-by: Jim Thompson <jim@netgate.com>
2019-03-07session: use transport custom tx for app transportsFlorin Coras1-9/+0
Change-Id: I675f7090fa6b2ffdfb4ee748df858bfb7e39ce5a Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-03-06session: use vpp to switch io events for ct sessionsFlorin Coras1-69/+18
Instead of allocating pairs of message queues per cut-thru session and having the applications map them, this uses vpp as an io event message switch. Change-Id: I51db1c7564df479a7d1a3288342394251fd188bb Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-03-02session: remove deprecated binary apisFlorin Coras1-333/+12
Change-Id: Ia1a628498fa3b639b6b1508f65c6aea1806f73ec Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-03-02session: cleanup/rename functionsFlorin Coras1-23/+23
- remove unused functions - rename old "stream" functions Change-Id: Icbb03daa9f9f1c58b5be5c38aa8a9cbcf9159b47 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-03-01session: refactor local connectsFlorin Coras1-70/+48
- Switches local connects to cut-thru transport - Removes local sessions as a separate session type Change-Id: I997c6355d8c8e4f2110678f785b0f5d96bba47f7 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-27session: refactor local/cut-through listensFlorin Coras1-50/+16
Introduce a cut-through transport as replacement to local sessions. This first patch removes the per app local listener pools and switches local listeners to sessions with cut-through transport. Change-Id: I98b20c25bc5df5f4110bf66f76c650a973032618 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-26session: fix local bindsFlorin Coras1-1/+3
Change-Id: I1d92bec29c718de7a517bbbb335d09c68c221c71 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-23session: separate local session logicFlorin Coras1-0/+1
Move local session to separate header and source files. First step to refactoring local sessions. Change-Id: I280fdfef20ba8a0977d15c1c8ce030ea2fb72dde Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-21vcl/session: send unlisten over message queueFlorin Coras1-1/+30
Change-Id: I68cd6c0e6be3e8088792df3885ae190bb00462b0 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-15Fix connexion segfault in session api affecting tcp_echoNathan Skrzypczak1-1/+2
Change-Id: If6d45f7f6e6dab70eecd6ec7241126968a34ad6b Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
2019-02-11session: cleanup application interfaceFlorin Coras1-50/+20
Change-Id: I89d240753b3f3c5e984aa303a7c8fa35fa59bf7f Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-09session: refactor listen logicFlorin Coras1-34/+31
Make app-listener the handle for app listens. Consequently transport and local listen sessions are now associated to the app-listener. Change-Id: I9397a26d42cccb100970b6b4794c15bac2e11465 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-06transport: cleanupFlorin Coras1-6/+4
- move transport specific types to transport_types - add transport wrapper functions for interaction with transport protocol vfts Change-Id: I93f70d884585fc2f41c4a605e310c80e8a8972f2 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-05session: cleanup part 2Florin Coras1-2/+2
Move app worker logic to app_worker.c Change-Id: Ic5e5735b2884f006c064d023f491aa6888114810 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-02-04session: cleanup part 1Florin Coras1-40/+40
Rename core data structures. This will break compatibility for out of tree builtin apps. - stream_session_t to session_t - server_rx/tx_fifo to rx/tx_fifo - stream_session.h to session_types.h - update copyright Change-Id: I414097c6e28bcbea866fbf13b8773c7db3f49325 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-01-27session: properly initialize accepted/connected msgsFlorin Coras1-0/+2
Change-Id: I36b3ae56e6ca160081c892367fd6a979751fe717 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-01-24session/vcl: support worker ownership change for listenersFlorin Coras1-2/+3
Change-Id: I2ad54b20b96f10b009c3e651b2a2f885577ca5b6 Signed-off-by: Florin Coras <fcoras@cisco.com>
2019-01-17vcl/session: replicate events for shared sessionsFlorin Coras1-33/+52
Change-Id: I7fb5402d4a530b5f2ffd9bb5787632099f4b4189 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-12-19session: handle close before app accept replyFlorin Coras1-0/+1
Change-Id: Ic35bbf55851087d70bfacc1eab4dea4285f98c86 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-12-05session/tcp: postpone cleanup on resetFlorin Coras1-1/+4
Change-Id: I45fd7538853f84c6c8bf804cc20acbc9601db3ba Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-12-04vcl: test refactor and improvementsFlorin Coras1-1/+4
Change-Id: I92f415bf253d6e051ec9d94ebeb98f081b2a0293 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-11-30vcl: wait for segments with segment handleFlorin Coras1-0/+1
Instead of waiting for notification from binary api. Change-Id: I5ecab857d6bcdbed62d6bb06709570c4cf6b19ea Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-11-30session: segment handle in accept/connect notificationsFlorin Coras1-6/+12
Change-Id: I03884b6cde9d4c38ae13d1994fd8d37d44016ef0 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-11-21session: improve app cliFlorin Coras1-2/+2
Change-Id: I766c0b2bfd5f781e4fe5df615a7ecd139b7d2cc4 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-11-21session: cleanup use of api_client_indexFlorin Coras1-31/+21
Change-Id: I8a680be62ab91d2ccb144641981a635506973a49 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-11-18vcl/session: apps with process workersFlorin Coras1-0/+3
Allow apps to register child processes as app workers. In particular, on fork vcl now registers the child process with vpp as a new worker. Change-Id: I52a65fbc3292962b1f6e1fe0f6153f739e6e0d4a Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-11-14Remove c-11 memcpy checks from perf-critical codeDave Barach1-18/+20
Change-Id: Id4f37f5d4a03160572954a416efa1ef9b3d79ad1 Signed-off-by: Dave Barach <dave@barachs.net>
2018-10-28session: extend connect api for internal appsFlorin Coras1-2/+2
Change-Id: Ie4c5cfc4c97acb321a46b4df589dc44de1b616ba Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-10-23c11 safe string handling supportDave Barach1-24/+24
Change-Id: Ied34720ca5a6e6e717eea4e86003e854031b6eab Signed-off-by: Dave Barach <dave@barachs.net>
2018-10-16vcl: fix bidirectional tests (VPP-1455)Florin Coras1-24/+24
- add epoll dequeued events beyond maxevents to unhandled - filter multiple epoll rx events Change-Id: I618f5f02b19581473de891b3b59bb6a0faad10b5 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-10-12session: don't wait indefinitely for apps to consume evts (VPP-1454)Florin Coras1-10/+28
Change-Id: I544b24d2b2c4a09829773cf180d1747f4b087d4c Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-09-29session: lock msg queues while generating control eventsFlorin Coras1-12/+6
Change-Id: I229ce9c306a97956c87ccb8f7a1c5c9dac94ab31 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-09-12vcl: improve read and fifo event handlingFlorin Coras1-0/+1
Change-Id: Ic1c51818b8aa8dbd164e70bb3b7471868e5af6f6 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-09-08vcl: set worker pthread stop keyFlorin Coras1-1/+1
Otherwise the key destructor is not called on pthread_exit. Change-Id: I11e6b9683a926eecd3f40a44aab41924ff9c3101 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-09-07vcl: refactor test client to support workersFlorin Coras1-1/+1
Change-Id: I7fcfddc2bc7d9a64f8aa0d57ba5d11d325a15ce1 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-09-06vcl/session/svm: fix coverity warningsFlorin Coras1-3/+3
Change-Id: I27532b3ab244dc95955e836a42b229a6e4e32818 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-09-06session: support multiple worker bindsFlorin Coras1-7/+12
Allows app workers to listen on the same session endpoint. Incoming connects are spread across the workers in a round-robin fashion Change-Id: Ib5f5817230d9abc6127a85cdbdcad70d980c0f7f Signed-off-by: Florin Coras <fcoras@cisco.com>
54 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502
/*
 *------------------------------------------------------------------
 * ipsec_api.c - ipsec api
 *
 * Copyright (c) 2016 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 <vnet/vnet.h>
#include <vlibmemory/api.h>

#include <vnet/interface.h>
#include <vnet/api_errno.h>
#include <vnet/ip/ip.h>
#include <vnet/ip/ip_types_api.h>
#include <vnet/ipsec/ipsec_types_api.h>
#include <vnet/tunnel/tunnel_types_api.h>
#include <vnet/fib/fib.h>
#include <vnet/ipip/ipip.h>
#include <vnet/tunnel/tunnel_types_api.h>
#include <vnet/ipsec/ipsec.h>
#include <vnet/ipsec/ipsec_tun.h>
#include <vnet/ipsec/ipsec_itf.h>

#include <vnet/format_fns.h>
#include <vnet/ipsec/ipsec.api_enum.h>
#include <vnet/ipsec/ipsec.api_types.h>

#define REPLY_MSG_ID_BASE ipsec_main.msg_id_base
#include <vlibapi/api_helper_macros.h>

static void
vl_api_ipsec_spd_add_del_t_handler (vl_api_ipsec_spd_add_del_t * mp)
{
  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
  vl_api_ipsec_spd_add_del_reply_t *rmp;
  int rv;

  rv = ipsec_add_del_spd (vm, ntohl (mp->spd_id), mp->is_add);

  REPLY_MACRO (VL_API_IPSEC_SPD_ADD_DEL_REPLY);
}

static void vl_api_ipsec_interface_add_del_spd_t_handler
  (vl_api_ipsec_interface_add_del_spd_t * mp)
{
  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
  vl_api_ipsec_interface_add_del_spd_reply_t *rmp;
  int rv;
  u32 sw_if_index __attribute__ ((unused));
  u32 spd_id __attribute__ ((unused));

  sw_if_index = ntohl (mp->sw_if_index);
  spd_id = ntohl (mp->spd_id);

  VALIDATE_SW_IF_INDEX (mp);

  rv = ipsec_set_interface_spd (vm, sw_if_index, spd_id, mp->is_add);

  BAD_SW_IF_INDEX_LABEL;

  REPLY_MACRO (VL_API_IPSEC_INTERFACE_ADD_DEL_SPD_REPLY);
}

static void vl_api_ipsec_tunnel_protect_update_t_handler
  (vl_api_ipsec_tunnel_protect_update_t * mp)
{
  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
  vl_api_ipsec_tunnel_protect_update_reply_t *rmp;
  u32 sw_if_index, ii, *sa_ins = NULL;
  ip_address_t nh;
  int rv;

  sw_if_index = ntohl (mp->tunnel.sw_if_index);

  VALIDATE_SW_IF_INDEX (&(mp->tunnel));

  for (ii = 0; ii < mp->tunnel.n_sa_in; ii++)
    vec_add1 (sa_ins, ntohl (mp->tunnel.sa_in[ii]));

  ip_address_decode2 (&mp->tunnel.nh, &nh);

  rv = ipsec_tun_protect_update (sw_if_index, &nh,
				 ntohl (mp->tunnel.sa_out), sa_ins);

  BAD_SW_IF_INDEX_LABEL;

  REPLY_MACRO (VL_API_IPSEC_TUNNEL_PROTECT_UPDATE_REPLY);
}

static void vl_api_ipsec_tunnel_protect_del_t_handler
  (vl_api_ipsec_tunnel_protect_del_t * mp)
{
  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
  vl_api_ipsec_tunnel_protect_del_reply_t *rmp;
  ip_address_t nh;
  u32 sw_if_index;
  int rv;

  sw_if_index = ntohl (mp->sw_if_index);

  VALIDATE_SW_IF_INDEX (mp);

  ip_address_decode2 (&mp->nh, &nh);
  rv = ipsec_tun_protect_del (sw_if_index, &nh);

  BAD_SW_IF_INDEX_LABEL;

  REPLY_MACRO (VL_API_IPSEC_TUNNEL_PROTECT_DEL_REPLY);
}

typedef struct ipsec_dump_walk_ctx_t_
{
  vl_api_registration_t *reg;
  u32 context;
  u32 sw_if_index;
} ipsec_dump_walk_ctx_t;

static walk_rc_t
send_ipsec_tunnel_protect_details (index_t itpi, void *arg)
{
  ipsec_dump_walk_ctx_t *ctx = arg;
  vl_api_ipsec_tunnel_protect_details_t *mp;
  ipsec_tun_protect_t *itp;
  u32 ii = 0;
  ipsec_sa_t *sa;

  itp = ipsec_tun_protect_get (itpi);

  mp = vl_msg_api_alloc (sizeof (*mp) + (sizeof (u32) * itp->itp_n_sa_in));
  clib_memset (mp, 0, sizeof (*mp));
  mp->_vl_msg_id =
    ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_TUNNEL_PROTECT_DETAILS);
  mp->context = ctx->context;

  mp->tun.sw_if_index = htonl (itp->itp_sw_if_index);
  ip_address_encode2 (itp->itp_key, &mp->tun.nh);

  sa = ipsec_sa_get (itp->itp_out_sa);
  mp->tun.sa_out = htonl (sa->id);
  mp->tun.n_sa_in = itp->itp_n_sa_in;
  /* *INDENT-OFF* */
  FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
  ({
    mp->tun.sa_in[ii++] = htonl (sa->id);
  }));
  /* *INDENT-ON* */

  vl_api_send_msg (ctx->reg, (u8 *) mp);

  return (WALK_CONTINUE);
}

static void
vl_api_ipsec_tunnel_protect_dump_t_handler (vl_api_ipsec_tunnel_protect_dump_t
					    * mp)
{
  vl_api_registration_t *reg;
  u32 sw_if_index;

  reg = vl_api_client_index_to_registration (mp->client_index);
  if (!reg)
    return;

  ipsec_dump_walk_ctx_t ctx = {
    .reg = reg,
    .context = mp->context,
  };

  sw_if_index = ntohl (mp->sw_if_index);

  if (~0 == sw_if_index)
    {
      ipsec_tun_protect_walk (send_ipsec_tunnel_protect_details, &ctx);
    }
  else
    {
      ipsec_tun_protect_walk_itf (sw_if_index,
				  send_ipsec_tunnel_protect_details, &ctx);
    }
}

static int
ipsec_spd_action_decode (vl_api_ipsec_spd_action_t in,
			 ipsec_policy_action_t * out)
{
  in = clib_net_to_host_u32 (in);

  switch (in)
    {
#define _(v,f,s) case IPSEC_API_SPD_ACTION_##f: \
      *out = IPSEC_POLICY_ACTION_##f;              \
      return (0);
      foreach_ipsec_policy_action
#undef _
    }
  return (VNET_API_ERROR_UNIMPLEMENTED);
}

static void vl_api_ipsec_spd_entry_add_del_t_handler
  (vl_api_ipsec_spd_entry_add_del_t * mp)
{
  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
  vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
  ip46_type_t itype;
  u32 stat_index;
  int rv;

  stat_index = ~0;

  ipsec_policy_t p;

  clib_memset (&p, 0, sizeof (p));

  p.id = ntohl (mp->entry.spd_id);
  p.priority = ntohl (mp->entry.priority);

  itype = ip_address_decode (&mp->entry.remote_address_start, &p.raddr.start);
  ip_address_decode (&mp->entry.remote_address_stop, &p.raddr.stop);
  ip_address_decode (&mp->entry.local_address_start, &p.laddr.start);
  ip_address_decode (&mp->entry.local_address_stop, &p.laddr.stop);

  p.is_ipv6 = (itype == IP46_TYPE_IP6);

  p.protocol =
    mp->entry.protocol ? mp->entry.protocol : IPSEC_POLICY_PROTOCOL_ANY;
  p.rport.start = ntohs (mp->entry.remote_port_start);
  p.rport.stop = ntohs (mp->entry.remote_port_stop);
  p.lport.start = ntohs (mp->entry.local_port_start);
  p.lport.stop = ntohs (mp->entry.local_port_stop);

  rv = ipsec_spd_action_decode (mp->entry.policy, &p.policy);

  if (rv)
    goto out;

  /* policy action resolve unsupported */
  if (p.policy == IPSEC_POLICY_ACTION_RESOLVE)
    {
      clib_warning ("unsupported action: 'resolve'");
      rv = VNET_API_ERROR_UNIMPLEMENTED;
      goto out;
    }
  p.sa_id = ntohl (mp->entry.sa_id);
  rv =
    ipsec_policy_mk_type (mp->entry.is_outbound, p.is_ipv6, p.policy,
			  &p.type);
  if (rv)
    goto out;

  rv = ipsec_add_del_policy (vm, &p, mp->is_add, &stat_index);
  if (rv)
    goto out;

out:
  /* *INDENT-OFF* */
  REPLY_MACRO2 (VL_API_IPSEC_SPD_ENTRY_ADD_DEL_REPLY,
  ({
    rmp->stat_index = ntohl(stat_index);
  }));
  /* *INDENT-ON* */
}

static void
vl_api_ipsec_spd_entry_add_del_v2_t_handler (
  vl_api_ipsec_spd_entry_add_del_v2_t *mp)
{
  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
  vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
  ip46_type_t itype;
  u32 stat_index;
  int rv;

  stat_index = ~0;

  ipsec_policy_t p;

  clib_memset (&p, 0, sizeof (p));

  p.id = ntohl (mp->entry.spd_id);
  p.priority = ntohl (mp->entry.priority);

  itype = ip_address_decode (&mp->entry.remote_address_start, &p.raddr.start);
  ip_address_decode (&mp->entry.remote_address_stop, &p.raddr.stop);
  ip_address_decode (&mp->entry.local_address_start, &p.laddr.start);
  ip_address_decode (&mp->entry.local_address_stop, &p.laddr.stop);

  p.is_ipv6 = (itype == IP46_TYPE_IP6);

  p.protocol = mp->entry.protocol;
  p.rport.start = ntohs (mp->entry.remote_port_start);
  p.rport.stop = ntohs (mp->entry.remote_port_stop);
  p.lport.start = ntohs (mp->entry.local_port_start);
  p.lport.stop = ntohs (mp->entry.local_port_stop);

  rv = ipsec_spd_action_decode (mp->entry.policy, &p.policy);

  if (rv)
    goto out;

  /* policy action resolve unsupported */
  if (p.policy == IPSEC_POLICY_ACTION_RESOLVE)
    {
      clib_warning ("unsupported action: 'resolve'");
      rv = VNET_API_ERROR_UNIMPLEMENTED;
      goto out;
    }
  p.sa_id = ntohl (mp->entry.sa_id);
  rv =
    ipsec_policy_mk_type (mp->entry.is_outbound, p.is_ipv6, p.policy, &p.type);
  if (rv)
    goto out;

  rv = ipsec_add_del_policy (vm, &p, mp->is_add, &stat_index);
  if (rv)
    goto out;

out:
  REPLY_MACRO2 (VL_API_IPSEC_SPD_ENTRY_ADD_DEL_V2_REPLY,
		({ rmp->stat_index = ntohl (stat_index); }));
}

static void vl_api_ipsec_sad_entry_add_del_t_handler
  (vl_api_ipsec_sad_entry_add_del_t * mp)
{
  vl_api_ipsec_sad_entry_add_del_reply_t *rmp;
  ipsec_key_t crypto_key, integ_key;
  ipsec_crypto_alg_t crypto_alg;
  ipsec_integ_alg_t integ_alg;
  ipsec_protocol_t proto;
  ipsec_sa_flags_t flags;
  u32 id, spi, sa_index = ~0;
  tunnel_t tun = {
    .t_flags = TUNNEL_FLAG_NONE,
    .t_encap_decap_flags = TUNNEL_ENCAP_DECAP_FLAG_NONE,
    .t_dscp = 0,
    .t_mode = TUNNEL_MODE_P2P,
    .t_table_id = 0,
    .t_hop_limit = 255,
  };
  int rv;

  id = ntohl (mp->entry.sad_id);
  if (!mp->is_add)
    {
      rv = ipsec_sa_unlock_id (id);
      goto out;
    }
  spi = ntohl (mp->entry.spi);

  rv = ipsec_proto_decode (mp->entry.protocol, &proto);

  if (rv)
    goto out;

  rv = ipsec_crypto_algo_decode (mp->entry.crypto_algorithm, &crypto_alg);

  if (rv)
    goto out;

  rv = ipsec_integ_algo_decode (mp->entry.integrity_algorithm, &integ_alg);

  if (rv)
    goto out;

  ipsec_key_decode (&mp->entry.crypto_key, &crypto_key);
  ipsec_key_decode (&mp->entry.integrity_key, &integ_key);

  flags = ipsec_sa_flags_decode (mp->entry.flags);

  ip_address_decode2 (&mp->entry.tunnel_src, &tun.t_src);
  ip_address_decode2 (&mp->entry.tunnel_dst, &tun.t_dst);

  rv = ipsec_sa_add_and_lock (
    id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
    mp->entry.salt, htons (mp->entry.udp_src_port),
    htons (mp->entry.udp_dst_port), 0, &tun, &sa_index);

out:
  /* *INDENT-OFF* */
  REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_DEL_REPLY,
  {
    rmp->stat_index = htonl (sa_index);
  });
  /* *INDENT-ON* */
}

static void vl_api_ipsec_sad_entry_add_del_v2_t_handler
  (vl_api_ipsec_sad_entry_add_del_v2_t * mp)
{
  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
  vl_api_ipsec_sad_entry_add_del_v2_reply_t *rmp;
  ipsec_key_t crypto_key, integ_key;
  ipsec_crypto_alg_t crypto_alg;
  ipsec_integ_alg_t integ_alg;
  ipsec_protocol_t proto;
  ipsec_sa_flags_t flags;
  u32 id, spi, sa_index = ~0;
  int rv;
  tunnel_t tun = {
    .t_flags = TUNNEL_FLAG_NONE,
    .t_encap_decap_flags = TUNNEL_ENCAP_DECAP_FLAG_NONE,
    .t_dscp = 0,
    .t_mode = TUNNEL_MODE_P2P,
    .t_table_id = htonl (mp->entry.tx_table_id),
    .t_hop_limit = 255,
  };

  id = ntohl (mp->entry.sad_id);
  if (!mp->is_add)
    {
      rv = ipsec_sa_unlock_id (id);
      goto out;
    }

  spi = ntohl (mp->entry.spi);

  rv = ipsec_proto_decode (mp->entry.protocol, &proto);

  if (rv)
    goto out;

  rv = ipsec_crypto_algo_decode (mp->entry.crypto_algorithm, &crypto_alg);

  if (rv)
    goto out;

  rv = ipsec_integ_algo_decode (mp->entry.integrity_algorithm, &integ_alg);

  if (rv)
    goto out;

  rv = tunnel_encap_decap_flags_decode (mp->entry.tunnel_flags,
					&tun.t_encap_decap_flags);

  if (rv)
    goto out;

  ipsec_key_decode (&mp->entry.crypto_key, &crypto_key);
  ipsec_key_decode (&mp->entry.integrity_key, &integ_key);

  flags = ipsec_sa_flags_decode (mp->entry.flags);
  tun.t_dscp = ip_dscp_decode (mp->entry.dscp);

  ip_address_decode2 (&mp->entry.tunnel_src, &tun.t_src);
  ip_address_decode2 (&mp->entry.tunnel_dst, &tun.t_dst);

  rv = ipsec_sa_add_and_lock (
    id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
    mp->entry.salt, htons (mp->entry.udp_src_port),
    htons (mp->entry.udp_dst_port), 0, &tun, &sa_index);

out:
  /* *INDENT-OFF* */
  REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_DEL_V2_REPLY,
  {
    rmp->stat_index = htonl (sa_index);
  });
  /* *INDENT-ON* */
}

static int
ipsec_sad_entry_add_v3 (const vl_api_ipsec_sad_entry_v3_t *entry,
			u32 *sa_index)
{
  ipsec_key_t crypto_key, integ_key;
  ipsec_crypto_alg_t crypto_alg;
  ipsec_integ_alg_t integ_alg;
  ipsec_protocol_t proto;
  ipsec_sa_flags_t flags;
  u32 id, spi;
  tunnel_t tun = { 0 };
  int rv;

  id = ntohl (entry->sad_id);
  spi = ntohl (entry->spi);

  rv = ipsec_proto_decode (entry->protocol, &proto);

  if (rv)
    return (rv);

  rv = ipsec_crypto_algo_decode (entry->crypto_algorithm, &crypto_alg);

  if (rv)
    return (rv);

  rv = ipsec_integ_algo_decode (entry->integrity_algorithm, &integ_alg);

  if (rv)
    return (rv);

  flags = ipsec_sa_flags_decode (entry->flags);

  if (flags & IPSEC_SA_FLAG_IS_TUNNEL)
    {
      rv = tunnel_decode (&entry->tunnel, &tun);

      if (rv)
	return (rv);
    }

  ipsec_key_decode (&entry->crypto_key, &crypto_key);
  ipsec_key_decode (&entry->integrity_key, &integ_key);

  return ipsec_sa_add_and_lock (
    id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
    entry->salt, htons (entry->udp_src_port), htons (entry->udp_dst_port), 0,
    &tun, sa_index);
}

static void
vl_api_ipsec_sad_entry_add_del_v3_t_handler (
  vl_api_ipsec_sad_entry_add_del_v3_t *mp)
{
  vl_api_ipsec_sad_entry_add_del_v3_reply_t *rmp;
  u32 id, sa_index = ~0;
  int rv;

  id = ntohl (mp->entry.sad_id);

  if (!mp->is_add)
    {
      rv = ipsec_sa_unlock_id (id);
    }
  else
    {
      rv = ipsec_sad_entry_add_v3 (&mp->entry, &sa_index);
    }

  REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_DEL_V3_REPLY,
		{ rmp->stat_index = htonl (sa_index); });
}

static int
ipsec_sad_entry_add_v4 (const vl_api_ipsec_sad_entry_v4_t *entry,
			u32 *sa_index)
{
  ipsec_key_t crypto_key, integ_key;
  ipsec_crypto_alg_t crypto_alg;
  ipsec_integ_alg_t integ_alg;
  ipsec_protocol_t proto;
  ipsec_sa_flags_t flags;
  u32 id, spi;
  tunnel_t tun = { 0 };
  int rv;

  id = ntohl (entry->sad_id);
  spi = ntohl (entry->spi);

  rv = ipsec_proto_decode (entry->protocol, &proto);

  if (rv)
    return rv;

  rv = ipsec_crypto_algo_decode (entry->crypto_algorithm, &crypto_alg);

  if (rv)
    return rv;

  rv = ipsec_integ_algo_decode (entry->integrity_algorithm, &integ_alg);

  if (rv)
    return rv;

  flags = ipsec_sa_flags_decode (entry->flags);

  if (flags & IPSEC_SA_FLAG_IS_TUNNEL)
    {
      rv = tunnel_decode (&entry->tunnel, &tun);

      if (rv)
	return rv;
    }

  ipsec_key_decode (&entry->crypto_key, &crypto_key);
  ipsec_key_decode (&entry->integrity_key, &integ_key);

  return ipsec_sa_add_and_lock (
    id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
    entry->salt, htons (entry->udp_src_port), htons (entry->udp_dst_port),
    ntohl (entry->anti_replay_window_size), &tun, sa_index);
}

static void
vl_api_ipsec_sad_entry_del_t_handler (vl_api_ipsec_sad_entry_del_t *mp)
{
  vl_api_ipsec_sad_entry_del_reply_t *rmp;
  int rv;

  rv = ipsec_sa_unlock_id (ntohl (mp->id));

  REPLY_MACRO (VL_API_IPSEC_SAD_ENTRY_DEL_REPLY);
}

static void
vl_api_ipsec_sad_entry_add_t_handler (vl_api_ipsec_sad_entry_add_t *mp)
{
  vl_api_ipsec_sad_entry_add_reply_t *rmp;
  u32 sa_index = ~0;
  int rv;

  rv = ipsec_sad_entry_add_v3 (&mp->entry, &sa_index);

  REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_REPLY,
		{ rmp->stat_index = htonl (sa_index); });
}

static void
vl_api_ipsec_sad_entry_add_v2_t_handler (vl_api_ipsec_sad_entry_add_v2_t *mp)
{
  vl_api_ipsec_sad_entry_add_reply_t *rmp;
  u32 sa_index = ~0;
  int rv;

  rv = ipsec_sad_entry_add_v4 (&mp->entry, &sa_index);

  REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_V2_REPLY,
		{ rmp->stat_index = htonl (sa_index); });
}

static void
vl_api_ipsec_sad_entry_update_t_handler (vl_api_ipsec_sad_entry_update_t *mp)
{
  vl_api_ipsec_sad_entry_update_reply_t *rmp;
  u32 id;
  tunnel_t tun = { 0 };
  int rv;

  id = ntohl (mp->sad_id);

  if (mp->is_tun)
    {
      rv = tunnel_decode (&mp->tunnel, &tun);

      if (rv)
	goto out;
    }

  rv = ipsec_sa_update (id, htons (mp->udp_src_port), htons (mp->udp_dst_port),
			&tun, mp->is_tun);

out:
  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
send_ipsec_spds_details (ipsec_spd_t * spd, vl_api_registration_t * reg,
			 u32 context)
{
  vl_api_ipsec_spds_details_t *mp;
  u32 n_policies = 0;

  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_SPDS_DETAILS);
  mp->context = context;

  mp->spd_id = htonl (spd->id);
#define _(s, n) n_policies += vec_len (spd->policies[IPSEC_SPD_POLICY_##s]);
  foreach_ipsec_spd_policy_type
#undef _
    mp->npolicies = htonl (n_policies);

  vl_api_send_msg (reg, (u8 *) mp);
}

static void
vl_api_ipsec_spds_dump_t_handler (vl_api_ipsec_spds_dump_t * mp)
{
  vl_api_registration_t *reg;
  ipsec_main_t *im = &ipsec_main;
  ipsec_spd_t *spd;

  reg = vl_api_client_index_to_registration (mp->client_index);
  if (!reg)
    return;

  pool_foreach (spd, im->spds)  {
    send_ipsec_spds_details (spd, reg, mp->context);
  }
}

vl_api_ipsec_spd_action_t
ipsec_spd_action_encode (ipsec_policy_action_t in)
{
  vl_api_ipsec_spd_action_t out = IPSEC_API_SPD_ACTION_BYPASS;

  switch (in)
    {
#define _(v,f,s) case IPSEC_POLICY_ACTION_##f: \
      out = IPSEC_API_SPD_ACTION_##f;          \
      break;
      foreach_ipsec_policy_action
#undef _
    }
  return (clib_host_to_net_u32 (out));
}

static void
send_ipsec_spd_details (ipsec_policy_t * p, vl_api_registration_t * reg,
			u32 context)
{
  vl_api_ipsec_spd_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_SPD_DETAILS);
  mp->context = context;

  mp->entry.spd_id = htonl (p->id);
  mp->entry.priority = htonl (p->priority);
  mp->entry.is_outbound = ((p->type == IPSEC_SPD_POLICY_IP6_OUTBOUND) ||
			   (p->type == IPSEC_SPD_POLICY_IP4_OUTBOUND));

  ip_address_encode (&p->laddr.start, IP46_TYPE_ANY,
		     &mp->entry.local_address_start);
  ip_address_encode (&p->laddr.stop, IP46_TYPE_ANY,
		     &mp->entry.local_address_stop);
  ip_address_encode (&p->raddr.start, IP46_TYPE_ANY,
		     &mp->entry.remote_address_start);
  ip_address_encode (&p->raddr.stop, IP46_TYPE_ANY,
		     &mp->entry.remote_address_stop);
  mp->entry.local_port_start = htons (p->lport.start);
  mp->entry.local_port_stop = htons (p->lport.stop);
  mp->entry.remote_port_start = htons (p->rport.start);
  mp->entry.remote_port_stop = htons (p->rport.stop);
  mp->entry.protocol = p->protocol;
  mp->entry.policy = ipsec_spd_action_encode (p->policy);
  mp->entry.sa_id = htonl (p->sa_id);

  vl_api_send_msg (reg, (u8 *) mp);
}

static void
vl_api_ipsec_spd_dump_t_handler (vl_api_ipsec_spd_dump_t * mp)
{
  vl_api_registration_t *reg;
  ipsec_main_t *im = &ipsec_main;
  ipsec_spd_policy_type_t ptype;
  ipsec_policy_t *policy;
  ipsec_spd_t *spd;
  uword *p;
  u32 spd_index, *ii;

  reg = vl_api_client_index_to_registration (mp->client_index);
  if (!reg)
    return;

  p = hash_get (im->spd_index_by_spd_id, ntohl (mp->spd_id));
  if (!p)
    return;

  spd_index = p[0];
  spd = pool_elt_at_index (im->spds, spd_index);

  FOR_EACH_IPSEC_SPD_POLICY_TYPE(ptype) {
    vec_foreach(ii, spd->policies[ptype])
      {
        policy = pool_elt_at_index(im->policies, *ii);

        if (mp->sa_id == ~(0) || ntohl (mp->sa_id) == policy->sa_id)
          send_ipsec_spd_details (policy, reg, mp->context);
      }
  }
}

static void
send_ipsec_spd_interface_details (vl_api_registration_t * reg, u32 spd_index,
				  u32 sw_if_index, u32 context)
{
  vl_api_ipsec_spd_interface_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_SPD_INTERFACE_DETAILS);
  mp->context = context;

  mp->spd_index = htonl (spd_index);
  mp->sw_if_index = htonl (sw_if_index);

  vl_api_send_msg (reg, (u8 *) mp);
}

static void
vl_api_ipsec_spd_interface_dump_t_handler (vl_api_ipsec_spd_interface_dump_t *
					   mp)
{
  ipsec_main_t *im = &ipsec_main;
  vl_api_registration_t *reg;
  u32 k, v, spd_index;

  reg = vl_api_client_index_to_registration (mp->client_index);
  if (!reg)
    return;

  if (mp->spd_index_valid)
    {
      spd_index = ntohl (mp->spd_index);
      /* *INDENT-OFF* */
      hash_foreach(k, v, im->spd_index_by_sw_if_index, ({
        if (v == spd_index)
          send_ipsec_spd_interface_details(reg, v, k, mp->context);
      }));
      /* *INDENT-ON* */
    }
  else
    {
      hash_foreach(k, v, im->spd_index_by_sw_if_index, ({
        send_ipsec_spd_interface_details(reg, v, k, mp->context);
      }));
    }
}

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 walk_rc_t
send_ipsec_itf_details (ipsec_itf_t *itf, void *arg)
{
  ipsec_dump_walk_ctx_t *ctx = arg;
  vl_api_ipsec_itf_details_t *mp;

  if (~0 != ctx->sw_if_index && ctx->sw_if_index != itf->ii_sw_if_index)
    return (WALK_CONTINUE);

  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_ITF_DETAILS);
  mp->context = ctx->context;

  mp->itf.mode = tunnel_mode_encode (itf->ii_mode);
  mp->itf.user_instance = htonl (itf->ii_user_instance);
  mp->itf.sw_if_index = htonl (itf->ii_sw_if_index);
  vl_api_send_msg (ctx->reg, (u8 *) mp);

  return (WALK_CONTINUE);
}

static void
vl_api_ipsec_itf_dump_t_handler (vl_api_ipsec_itf_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,
    .sw_if_index = ntohl (mp->sw_if_index),
  };

  ipsec_itf_walk (send_ipsec_itf_details, &ctx);
}

typedef struct ipsec_sa_dump_match_ctx_t_
{
  index_t sai;
  u32 sw_if_index;
} ipsec_sa_dump_match_ctx_t;

static walk_rc_t
ipsec_sa_dump_match_sa (index_t itpi, void *arg)
{
  ipsec_sa_dump_match_ctx_t *ctx = arg;
  ipsec_tun_protect_t *itp;
  index_t sai;

  itp = ipsec_tun_protect_get (itpi);

  if (itp->itp_out_sa == ctx->sai)
    {
      ctx->sw_if_index = itp->itp_sw_if_index;
      return (WALK_STOP);
    }

  FOR_EACH_IPSEC_PROTECT_INPUT_SAI (itp, sai,
  ({
    if (sai == ctx->sai)
      {
        ctx->sw_if_index = itp->itp_sw_if_index;
        return (WALK_STOP);
      }
  }));

  return (WALK_CONTINUE);
}

static walk_rc_t
send_ipsec_sa_details (ipsec_sa_t * sa, void *arg)
{
  ipsec_dump_walk_ctx_t *ctx = arg;
  vl_api_ipsec_sa_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_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.tx_table_id = htonl (sa->tunnel.t_table_id);

  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))
    {
      ip_address_encode2 (&sa->tunnel.t_src, &mp->entry.tunnel_src);
      ip_address_encode2 (&sa->tunnel.t_dst, &mp->entry.tunnel_dst);
    }
  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 (ipsec_sa_anti_replay_get_64b_window (sa));
    }

  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_dump_t_handler (vl_api_ipsec_sa_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_details, &ctx);
}

static walk_rc_t
send_ipsec_sa_v2_details (ipsec_sa_t * sa, void *arg)
{
  ipsec_dump_walk_ctx_t *ctx = arg;
  vl_api_ipsec_sa_v2_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_V2_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.tx_table_id = htonl (sa->tunnel.t_table_id);

  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))
    {
      ip_address_encode2 (&sa->tunnel.t_src, &mp->entry.tunnel_src);
      ip_address_encode2 (&sa->tunnel.t_dst, &mp->entry.tunnel_dst);
    }
  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->entry.tunnel_flags =
    tunnel_encap_decap_flags_encode (sa->tunnel.t_encap_decap_flags);
  mp->entry.dscp = ip_dscp_encode (sa->tunnel.t_dscp);

  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 (ipsec_sa_anti_replay_get_64b_window (sa));
    }

  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_v2_dump_t_handler (vl_api_ipsec_sa_v2_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_v2_details, &ctx);
}

static walk_rc_t
send_ipsec_sa_v3_details (ipsec_sa_t *sa, void *arg)
{
  ipsec_dump_walk_ctx_t *ctx = arg;
  vl_api_ipsec_sa_v3_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_V3_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 (ipsec_sa_anti_replay_get_64b_window (sa));
    }

  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_v3_dump_t_handler (vl_api_ipsec_sa_v3_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_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 (ipsec_sa_anti_replay_get_64b_window (sa));
    }

  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 walk_rc_t
send_ipsec_sa_v5_details (ipsec_sa_t *sa, void *arg)
{
  ipsec_dump_walk_ctx_t *ctx = arg;
  vl_api_ipsec_sa_v5_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_V5_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 (ipsec_sa_anti_replay_get_64b_window (sa));

      mp->entry.anti_replay_window_size =
	clib_host_to_net_u32 (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (sa));
    }

  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_v5_dump_t_handler (vl_api_ipsec_sa_v5_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_v5_details, &ctx);
}

static void
vl_api_ipsec_backend_dump_t_handler (vl_api_ipsec_backend_dump_t *mp)
{
  vl_api_registration_t *rp;
  ipsec_main_t *im = &ipsec_main;
  u32 context = mp->context;

  rp = vl_api_client_index_to_registration (mp->client_index);

  if (rp == 0)
    {
      clib_warning ("Client %d AWOL", mp->client_index);
      return;
    }

  ipsec_ah_backend_t *ab;
  ipsec_esp_backend_t *eb;
  /* *INDENT-OFF* */
  pool_foreach (ab, im->ah_backends) {
    vl_api_ipsec_backend_details_t *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_BACKEND_DETAILS);
    mp->context = context;
    snprintf ((char *)mp->name, sizeof (mp->name), "%.*s", vec_len (ab->name),
              ab->name);
    mp->protocol = ntohl (IPSEC_API_PROTO_AH);
    mp->index = ab - im->ah_backends;
    mp->active = mp->index == im->ah_current_backend ? 1 : 0;
    vl_api_send_msg (rp, (u8 *)mp);
  }
  pool_foreach (eb, im->esp_backends) {
    vl_api_ipsec_backend_details_t *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_BACKEND_DETAILS);
    mp->context = context;
    snprintf ((char *)mp->name, sizeof (mp->name), "%.*s", vec_len (eb->name),
              eb->name);
    mp->protocol = ntohl (IPSEC_API_PROTO_ESP);
    mp->index = eb - im->esp_backends;
    mp->active = mp->index == im->esp_current_backend ? 1 : 0;
    vl_api_send_msg (rp, (u8 *)mp);
  }
  /* *INDENT-ON* */
}

static void
vl_api_ipsec_select_backend_t_handler (vl_api_ipsec_select_backend_t * mp)
{
  ipsec_main_t *im = &ipsec_main;
  vl_api_ipsec_select_backend_reply_t *rmp;
  ipsec_protocol_t protocol;
  int rv = 0;
  if (pool_elts (ipsec_sa_pool) > 0)
    {
      rv = VNET_API_ERROR_INSTANCE_IN_USE;
      goto done;
    }

  rv = ipsec_proto_decode (mp->protocol, &protocol);

  if (rv)
    goto done;

  switch (protocol)
    {
    case IPSEC_PROTOCOL_ESP:
      rv = ipsec_select_esp_backend (im, mp->index);
      break;
    case IPSEC_PROTOCOL_AH:
      rv = ipsec_select_ah_backend (im, mp->index);
      break;
    default:
      rv = VNET_API_ERROR_INVALID_PROTOCOL;
      break;
    }
done:
  REPLY_MACRO (VL_API_IPSEC_SELECT_BACKEND_REPLY);
}

static void
vl_api_ipsec_set_async_mode_t_handler (vl_api_ipsec_set_async_mode_t * mp)
{
  vl_api_ipsec_set_async_mode_reply_t *rmp;
  int rv = 0;

  ipsec_set_async_mode (mp->async_enable);

  REPLY_MACRO (VL_API_IPSEC_SET_ASYNC_MODE_REPLY);
}

#include <vnet/ipsec/ipsec.api.c>
static clib_error_t *
ipsec_api_hookup (vlib_main_t * vm)
{
  /*
   * Set up the (msg_name, crc, message-id) table
   */
  REPLY_MSG_ID_BASE = setup_message_id_table ();

  return 0;
}

VLIB_API_INIT_FUNCTION (ipsec_api_hookup);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */