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>
diff --git a/src/plugins/dpdk/ipsec/ipsec.c b/src/plugins/dpdk/ipsec/ipsec.c
index 2415422..f79b430 100644
--- a/src/plugins/dpdk/ipsec/ipsec.c
+++ b/src/plugins/dpdk/ipsec/ipsec.c
@@ -252,7 +252,7 @@
 
   xform->type = RTE_CRYPTO_SYM_XFORM_AEAD;
   xform->aead.algo = c->alg;
-  xform->aead.key.data = sa->crypto_key;
+  xform->aead.key.data = sa->crypto_key.data;
   xform->aead.key.length = c->key_len;
   xform->aead.iv.offset =
     crypto_op_get_priv_offset () + offsetof (dpdk_op_priv_t, cb);
@@ -280,7 +280,7 @@
 
   xform->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
   xform->cipher.algo = c->alg;
-  xform->cipher.key.data = sa->crypto_key;
+  xform->cipher.key.data = sa->crypto_key.data;
   xform->cipher.key.length = c->key_len;
   xform->cipher.iv.offset =
     crypto_op_get_priv_offset () + offsetof (dpdk_op_priv_t, cb);
@@ -306,7 +306,7 @@
 
   xform->type = RTE_CRYPTO_SYM_XFORM_AUTH;
   xform->auth.algo = a->alg;
-  xform->auth.key.data = sa->integ_key;
+  xform->auth.key.data = sa->integ_key.data;
   xform->auth.key.length = a->key_len;
   xform->auth.digest_length = a->trunc_size;
   xform->next = NULL;
@@ -511,7 +511,8 @@
 	case IPSEC_CRYPTO_ALG_AES_GCM_128:
 	case IPSEC_CRYPTO_ALG_AES_GCM_192:
 	case IPSEC_CRYPTO_ALG_AES_GCM_256:
-	  clib_memcpy (&sa->salt, &sa->crypto_key[sa->crypto_key_len - 4], 4);
+	  clib_memcpy (&sa->salt,
+		       &sa->crypto_key.data[sa->crypto_key.len - 4], 4);
 	  break;
 	default:
 	  seed = (u32) clib_cpu_time_now ();
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index 5ddbb57..1d93cc1 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -15215,21 +15215,21 @@
   vat_main_t *vam = &vat_main;
 
   print (vam->ofp, "sa_id %u sw_if_index %u spi %u proto %u crypto_alg %u "
-	 "crypto_key %U integ_alg %u integ_key %U use_esn %u "
-	 "use_anti_replay %u is_tunnel %u is_tunnel_ip6 %u "
+	 "crypto_key %U integ_alg %u integ_key %U flags %x "
 	 "tunnel_src_addr %U tunnel_dst_addr %U "
 	 "salt %u seq_outbound %lu last_seq_inbound %lu "
 	 "replay_window %lu total_data_size %lu\n",
-	 ntohl (mp->sa_id), ntohl (mp->sw_if_index), ntohl (mp->spi),
-	 mp->protocol,
-	 mp->crypto_alg, format_hex_bytes, mp->crypto_key, mp->crypto_key_len,
-	 mp->integ_alg, format_hex_bytes, mp->integ_key, mp->integ_key_len,
-	 mp->use_esn, mp->use_anti_replay, mp->is_tunnel, mp->is_tunnel_ip6,
-	 (mp->is_tunnel_ip6) ? format_ip6_address : format_ip4_address,
-	 mp->tunnel_src_addr,
-	 (mp->is_tunnel_ip6) ? format_ip6_address : format_ip4_address,
-	 mp->tunnel_dst_addr,
-	 ntohl (mp->salt),
+	 ntohl (mp->entry.sad_id),
+	 ntohl (mp->sw_if_index),
+	 ntohl (mp->entry.spi),
+	 ntohl (mp->entry.protocol),
+	 ntohl (mp->entry.crypto_algorithm),
+	 format_hex_bytes, mp->entry.crypto_key.data,
+	 mp->entry.crypto_key.length, ntohl (mp->entry.integrity_algorithm),
+	 format_hex_bytes, mp->entry.integrity_key.data,
+	 mp->entry.integrity_key.length, ntohl (mp->entry.flags),
+	 format_vl_api_address, &mp->entry.tunnel_src, format_vl_api_address,
+	 &mp->entry.tunnel_dst, ntohl (mp->salt),
 	 clib_net_to_host_u64 (mp->seq_outbound),
 	 clib_net_to_host_u64 (mp->last_seq_inbound),
 	 clib_net_to_host_u64 (mp->replay_window),
@@ -15239,13 +15239,32 @@
 #define vl_api_ipsec_sa_details_t_endian vl_noop_handler
 #define vl_api_ipsec_sa_details_t_print vl_noop_handler
 
+static void
+vat_json_object_add_address (vat_json_node_t * node,
+			     const vl_api_address_t * addr)
+{
+  if (ADDRESS_IP6 == addr->af)
+    {
+      struct in6_addr ip6;
+
+      clib_memcpy (&ip6, &addr->un.ip6, sizeof (ip6));
+      vat_json_object_add_ip6 (node, "ip_address", ip6);
+    }
+  else
+    {
+      struct in_addr ip4;
+
+      clib_memcpy (&ip4, &addr->un.ip4, sizeof (ip4));
+      vat_json_object_add_ip4 (node, "ip_address", ip4);
+    }
+}
+
 static void vl_api_ipsec_sa_details_t_handler_json
   (vl_api_ipsec_sa_details_t * mp)
 {
   vat_main_t *vam = &vat_main;
   vat_json_node_t *node = NULL;
-  struct in_addr src_ip4, dst_ip4;
-  struct in6_addr src_ip6, dst_ip6;
+  vl_api_ipsec_sad_flags_t flags;
 
   if (VAT_JSON_ARRAY != vam->json_tree.type)
     {
@@ -15255,39 +15274,36 @@
   node = vat_json_array_add (&vam->json_tree);
 
   vat_json_init_object (node);
-  vat_json_object_add_uint (node, "sa_id", ntohl (mp->sa_id));
+  vat_json_object_add_uint (node, "sa_id", ntohl (mp->entry.sad_id));
   vat_json_object_add_uint (node, "sw_if_index", ntohl (mp->sw_if_index));
-  vat_json_object_add_uint (node, "spi", ntohl (mp->spi));
-  vat_json_object_add_uint (node, "proto", mp->protocol);
-  vat_json_object_add_uint (node, "crypto_alg", mp->crypto_alg);
-  vat_json_object_add_uint (node, "integ_alg", mp->integ_alg);
-  vat_json_object_add_uint (node, "use_esn", mp->use_esn);
-  vat_json_object_add_uint (node, "use_anti_replay", mp->use_anti_replay);
-  vat_json_object_add_uint (node, "is_tunnel", mp->is_tunnel);
-  vat_json_object_add_uint (node, "is_tunnel_ip6", mp->is_tunnel_ip6);
-  vat_json_object_add_bytes (node, "crypto_key", mp->crypto_key,
-			     mp->crypto_key_len);
-  vat_json_object_add_bytes (node, "integ_key", mp->integ_key,
-			     mp->integ_key_len);
-  if (mp->is_tunnel_ip6)
-    {
-      clib_memcpy (&src_ip6, mp->tunnel_src_addr, sizeof (src_ip6));
-      vat_json_object_add_ip6 (node, "tunnel_src_addr", src_ip6);
-      clib_memcpy (&dst_ip6, mp->tunnel_dst_addr, sizeof (dst_ip6));
-      vat_json_object_add_ip6 (node, "tunnel_dst_addr", dst_ip6);
-    }
-  else
-    {
-      clib_memcpy (&src_ip4, mp->tunnel_src_addr, sizeof (src_ip4));
-      vat_json_object_add_ip4 (node, "tunnel_src_addr", src_ip4);
-      clib_memcpy (&dst_ip4, mp->tunnel_dst_addr, sizeof (dst_ip4));
-      vat_json_object_add_ip4 (node, "tunnel_dst_addr", dst_ip4);
-    }
+  vat_json_object_add_uint (node, "spi", ntohl (mp->entry.spi));
+  vat_json_object_add_uint (node, "proto", ntohl (mp->entry.protocol));
+  vat_json_object_add_uint (node, "crypto_alg",
+			    ntohl (mp->entry.crypto_algorithm));
+  vat_json_object_add_uint (node, "integ_alg",
+			    ntohl (mp->entry.integrity_algorithm));
+  flags = ntohl (mp->entry.flags);
+  vat_json_object_add_uint (node, "use_esn",
+			    ! !(flags &
+				IPSEC_API_SAD_FLAG_USE_EXTENDED_SEQ_NUM));
+  vat_json_object_add_uint (node, "use_anti_replay",
+			    ! !(flags & IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY));
+  vat_json_object_add_uint (node, "is_tunnel",
+			    ! !(flags & IPSEC_API_SAD_FLAG_IS_TUNNEL));
+  vat_json_object_add_uint (node, "is_tunnel_ip6",
+			    ! !(flags & IPSEC_API_SAD_FLAG_IS_TUNNEL_V6));
+  vat_json_object_add_uint (node, "udp_encap",
+			    ! !(flags & IPSEC_API_SAD_FLAG_UDP_ENCAP));
+  vat_json_object_add_bytes (node, "crypto_key", mp->entry.crypto_key.data,
+			     mp->entry.crypto_key.length);
+  vat_json_object_add_bytes (node, "integ_key", mp->entry.integrity_key.data,
+			     mp->entry.integrity_key.length);
+  vat_json_object_add_address (node, &mp->entry.tunnel_src);
+  vat_json_object_add_address (node, &mp->entry.tunnel_dst);
   vat_json_object_add_uint (node, "replay_window",
 			    clib_net_to_host_u64 (mp->replay_window));
   vat_json_object_add_uint (node, "total_data_size",
 			    clib_net_to_host_u64 (mp->total_data_size));
-
 }
 
 static int
@@ -20085,8 +20101,6 @@
 
   vat_main_t *vam = &vat_main;
   vat_json_node_t *node;
-  struct in_addr ip4;
-  struct in6_addr ip6;
 
   if (VAT_JSON_ARRAY != vam->json_tree.type)
     {
@@ -20104,17 +20118,7 @@
   vat_json_object_add_string_copy (node, "link_layer",
 				   format (0, "%U", format_vl_api_mac_address,
 					   &mp->neighbor.mac_address));
-
-  if (ADDRESS_IP6 == mp->neighbor.ip_address.af)
-    {
-      clib_memcpy (&ip6, &mp->neighbor.ip_address.un.ip6, sizeof (ip6));
-      vat_json_object_add_ip6 (node, "ip_address", ip6);
-    }
-  else
-    {
-      clib_memcpy (&ip4, &mp->neighbor.ip_address.un.ip4, sizeof (ip4));
-      vat_json_object_add_ip4 (node, "ip_address", ip4);
-    }
+  vat_json_object_add_address (node, &mp->neighbor.ip_address);
 }
 
 static int
diff --git a/src/vnet/fib/fib_node.h b/src/vnet/fib/fib_node.h
index 1179dfc..e9c45c5 100644
--- a/src/vnet/fib/fib_node.h
+++ b/src/vnet/fib/fib_node.h
@@ -48,6 +48,7 @@
     FIB_NODE_TYPE_BIER_FMASK,
     FIB_NODE_TYPE_BIER_ENTRY,
     FIB_NODE_TYPE_VXLAN_GBP_TUNNEL,
+    FIB_NODE_TYPE_IPSEC_SA,
     /**
      * Marker. New types before this one. leave the test last.
      */
@@ -75,7 +76,8 @@
     [FIB_NODE_TYPE_UDP_ENCAP] = "udp-encap",			\
     [FIB_NODE_TYPE_BIER_FMASK] = "bier-fmask",			\
     [FIB_NODE_TYPE_BIER_ENTRY] = "bier-entry",			\
-    [FIB_NODE_TYPE_VXLAN_GBP_TUNNEL] = "vxlan-gbp-tunnel"	\
+    [FIB_NODE_TYPE_VXLAN_GBP_TUNNEL] = "vxlan-gbp-tunnel",	\
+    [FIB_NODE_TYPE_IPSEC_SA] = "ipsec-sa"                       \
 }
 
 /**
diff --git a/src/vnet/ipsec/ah_decrypt.c b/src/vnet/ipsec/ah_decrypt.c
index c8c8902..7d2bf81 100644
--- a/src/vnet/ipsec/ah_decrypt.c
+++ b/src/vnet/ipsec/ah_decrypt.c
@@ -200,9 +200,9 @@
 		  icv_padding_len =
 		    ah_calc_icv_padding_len (icv_size, 0 /* is_ipv6 */ );
 		}
-	      hmac_calc (sa0->integ_alg, sa0->integ_key, sa0->integ_key_len,
-			 (u8 *) ih4, i_b0->current_length, sig, sa0->use_esn,
-			 sa0->seq_hi);
+	      hmac_calc (sa0->integ_alg, sa0->integ_key.data,
+			 sa0->integ_key.len, (u8 *) ih4, i_b0->current_length,
+			 sig, sa0->use_esn, sa0->seq_hi);
 
 	      if (PREDICT_FALSE (memcmp (digest, sig, icv_size)))
 		{
diff --git a/src/vnet/ipsec/ah_encrypt.c b/src/vnet/ipsec/ah_encrypt.c
index 8b0c14f..6628609 100644
--- a/src/vnet/ipsec/ah_encrypt.c
+++ b/src/vnet/ipsec/ah_encrypt.c
@@ -59,6 +59,7 @@
 
 typedef struct
 {
+  u32 sa_index;
   u32 spi;
   u32 seq;
   ipsec_integ_alg_t integ_alg;
@@ -72,8 +73,9 @@
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   ah_encrypt_trace_t *t = va_arg (*args, ah_encrypt_trace_t *);
 
-  s = format (s, "ah: spi %u seq %u integrity %U",
-	      t->spi, t->seq, format_ipsec_integ_alg, t->integ_alg);
+  s = format (s, "ah: sa-index %d spi %u seq %u integrity %U",
+	      t->sa_index, t->spi, t->seq,
+	      format_ipsec_integ_alg, t->integ_alg);
   return s;
 }
 
@@ -237,8 +239,9 @@
 	      oh0->ip4.src_address.as_u32 = sa0->tunnel_src_addr.ip4.as_u32;
 	      oh0->ip4.dst_address.as_u32 = sa0->tunnel_dst_addr.ip4.as_u32;
 
-	      next0 = AH_ENCRYPT_NEXT_IP4_LOOKUP;
-	      vnet_buffer (i_b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
+	      next0 = sa0->dpo[IPSEC_PROTOCOL_AH].dpoi_next_node;
+	      vnet_buffer (i_b0)->ip.adj_index[VLIB_TX] =
+		sa0->dpo[IPSEC_PROTOCOL_AH].dpoi_index;
 	    }
 	  else if (is_ip6 && sa0->is_tunnel && sa0->is_tunnel_ip6)
 	    {
@@ -251,8 +254,9 @@
 	      oh6_0->ip6.dst_address.as_u64[1] =
 		sa0->tunnel_dst_addr.ip6.as_u64[1];
 
-	      next0 = AH_ENCRYPT_NEXT_IP6_LOOKUP;
-	      vnet_buffer (i_b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
+	      next0 = sa0->dpo[IPSEC_PROTOCOL_AH].dpoi_next_node;
+	      vnet_buffer (i_b0)->ip.adj_index[VLIB_TX] =
+		sa0->dpo[IPSEC_PROTOCOL_AH].dpoi_index;
 	    }
 
 	  u8 sig[64];
@@ -262,8 +266,8 @@
 	    sizeof (ah_header_t);
 	  clib_memset (digest, 0, icv_size);
 
-	  unsigned size = hmac_calc (sa0->integ_alg, sa0->integ_key,
-				     sa0->integ_key_len,
+	  unsigned size = hmac_calc (sa0->integ_alg, sa0->integ_key.data,
+				     sa0->integ_key.len,
 				     vlib_buffer_get_current (i_b0),
 				     i_b0->current_length, sig, sa0->use_esn,
 				     sa0->seq_hi);
@@ -297,6 +301,7 @@
 	      tr->spi = sa0->spi;
 	      tr->seq = sa0->seq - 1;
 	      tr->integ_alg = sa0->integ_alg;
+	      tr->sa_index = sa_index0;
 	    }
 
 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c
index 3f46350..5a3ccdc 100644
--- a/src/vnet/ipsec/esp_decrypt.c
+++ b/src/vnet/ipsec/esp_decrypt.c
@@ -206,8 +206,9 @@
 		icv_size;
 	      i_b0->current_length -= icv_size;
 
-	      hmac_calc (sa0->integ_alg, sa0->integ_key, sa0->integ_key_len,
-			 (u8 *) esp0, i_b0->current_length, sig, sa0->use_esn,
+	      hmac_calc (sa0->integ_alg, sa0->integ_key.data,
+			 sa0->integ_key.len, (u8 *) esp0,
+			 i_b0->current_length, sig, sa0->use_esn,
 			 sa0->seq_hi);
 
 	      if (PREDICT_FALSE (memcmp (icv, sig, icv_size)))
@@ -299,7 +300,7 @@
 			       esp0->data + IV_SIZE,
 			       (u8 *) vlib_buffer_get_current (o_b0) +
 			       ip_hdr_size, BLOCK_SIZE * blocks,
-			       sa0->crypto_key, esp0->data);
+			       sa0->crypto_key.data, esp0->data);
 
 	      o_b0->current_length = (blocks * BLOCK_SIZE) - 2 + ip_hdr_size;
 	      o_b0->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c
index 16f985c..4b325e0 100644
--- a/src/vnet/ipsec/esp_encrypt.c
+++ b/src/vnet/ipsec/esp_encrypt.c
@@ -62,6 +62,7 @@
 
 typedef struct
 {
+  u32 sa_index;
   u32 spi;
   u32 seq;
   u8 udp_encap;
@@ -77,8 +78,8 @@
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   esp_encrypt_trace_t *t = va_arg (*args, esp_encrypt_trace_t *);
 
-  s = format (s, "esp: spi %u seq %u crypto %U integrity %U%s",
-	      t->spi, t->seq,
+  s = format (s, "esp: sa-index %d spi %u seq %u crypto %U integrity %U%s",
+	      t->sa_index, t->spi, t->seq,
 	      format_ipsec_crypto_alg, t->crypto_alg,
 	      format_ipsec_integ_alg, t->integ_alg,
 	      t->udp_encap ? " udp-encap-enabled" : "");
@@ -279,7 +280,9 @@
 	      oh0->ip4.src_address.as_u32 = sa0->tunnel_src_addr.ip4.as_u32;
 	      oh0->ip4.dst_address.as_u32 = sa0->tunnel_dst_addr.ip4.as_u32;
 
-	      vnet_buffer (o_b0)->sw_if_index[VLIB_TX] = sa0->tx_fib_index;
+	      next0 = sa0->dpo[IPSEC_PROTOCOL_ESP].dpoi_next_node;
+	      vnet_buffer (o_b0)->ip.adj_index[VLIB_TX] =
+		sa0->dpo[IPSEC_PROTOCOL_ESP].dpoi_index;
 	    }
 	  else if (is_ip6 && sa0->is_tunnel && sa0->is_tunnel_ip6)
 	    {
@@ -292,7 +295,9 @@
 	      oh6_0->ip6.dst_address.as_u64[1] =
 		sa0->tunnel_dst_addr.ip6.as_u64[1];
 
-	      vnet_buffer (o_b0)->sw_if_index[VLIB_TX] = sa0->tx_fib_index;
+	      next0 = sa0->dpo[IPSEC_PROTOCOL_ESP].dpoi_next_node;
+	      vnet_buffer (o_b0)->ip.adj_index[VLIB_TX] =
+		sa0->dpo[IPSEC_PROTOCOL_ESP].dpoi_index;
 	    }
 	  else
 	    {
@@ -367,17 +372,15 @@
 			       (u8 *) vlib_buffer_get_current (o_b0) +
 			       ip_udp_hdr_size + sizeof (esp_header_t) +
 			       IV_SIZE, BLOCK_SIZE * blocks,
-			       sa0->crypto_key, iv);
+			       sa0->crypto_key.data, iv);
 	    }
 
-	  o_b0->current_length += hmac_calc (sa0->integ_alg, sa0->integ_key,
-					     sa0->integ_key_len,
-					     (u8 *) o_esp0,
-					     o_b0->current_length -
-					     ip_udp_hdr_size,
-					     vlib_buffer_get_current (o_b0) +
-					     o_b0->current_length,
-					     sa0->use_esn, sa0->seq_hi);
+	  o_b0->current_length +=
+	    hmac_calc (sa0->integ_alg, sa0->integ_key.data,
+		       sa0->integ_key.len, (u8 *) o_esp0,
+		       o_b0->current_length - ip_udp_hdr_size,
+		       vlib_buffer_get_current (o_b0) + o_b0->current_length,
+		       sa0->use_esn, sa0->seq_hi);
 
 
 	  if (is_ip6)
@@ -412,6 +415,7 @@
 		  o_b0->trace_index = i_b0->trace_index;
 		  esp_encrypt_trace_t *tr =
 		    vlib_add_trace (vm, node, o_b0, sizeof (*tr));
+		  tr->sa_index = sa_index0;
 		  tr->spi = sa0->spi;
 		  tr->seq = sa0->seq - 1;
 		  tr->udp_encap = sa0->udp_encap;
diff --git a/src/vnet/ipsec/ikev2.c b/src/vnet/ipsec/ikev2.c
index 80497f2..3d5c0f7 100644
--- a/src/vnet/ipsec/ikev2.c
+++ b/src/vnet/ipsec/ikev2.c
@@ -1492,16 +1492,16 @@
   a.is_add = 1;
   if (sa->is_initiator)
     {
-      a.local_ip.as_u32 = sa->iaddr.as_u32;
-      a.remote_ip.as_u32 = sa->raddr.as_u32;
+      a.local_ip.ip4.as_u32 = sa->iaddr.as_u32;
+      a.remote_ip.ip4.as_u32 = sa->raddr.as_u32;
       proposals = child->i_proposals;
       a.local_spi = child->r_proposals[0].spi;
       a.remote_spi = child->i_proposals[0].spi;
     }
   else
     {
-      a.local_ip.as_u32 = sa->raddr.as_u32;
-      a.remote_ip.as_u32 = sa->iaddr.as_u32;
+      a.local_ip.ip4.as_u32 = sa->raddr.as_u32;
+      a.remote_ip.ip4.as_u32 = sa->iaddr.as_u32;
       proposals = child->r_proposals;
       a.local_spi = child->i_proposals[0].spi;
       a.remote_spi = child->r_proposals[0].spi;
@@ -1642,8 +1642,8 @@
 	return 0;
 
       a.is_add = 0;
-      a.local_ip.as_u32 = sa->iaddr.as_u32;
-      a.remote_ip.as_u32 = sa->raddr.as_u32;
+      a.local_ip.ip4.as_u32 = sa->iaddr.as_u32;
+      a.remote_ip.ip4.as_u32 = sa->raddr.as_u32;
       a.local_spi = child->r_proposals[0].spi;
       a.remote_spi = child->i_proposals[0].spi;
     }
@@ -1653,8 +1653,8 @@
 	return 0;
 
       a.is_add = 0;
-      a.local_ip.as_u32 = sa->raddr.as_u32;
-      a.remote_ip.as_u32 = sa->iaddr.as_u32;
+      a.local_ip.ip4.as_u32 = sa->raddr.as_u32;
+      a.remote_ip.ip4.as_u32 = sa->iaddr.as_u32;
       a.local_spi = child->i_proposals[0].spi;
       a.remote_spi = child->r_proposals[0].spi;
     }
diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api
index 54cc76b..ece0b02 100644
--- a/src/vnet/ipsec/ipsec.api
+++ b/src/vnet/ipsec/ipsec.api
@@ -232,6 +232,10 @@
   IPSEC_API_SAD_FLAG_IS_TUNNEL_V6 = 0x08,
   /* enable UDP encapsulation for NAT traversal */
   IPSEC_API_SAD_FLAG_UDP_ENCAP = 0x10,
+
+  /* come-on Ole please fix this */
+  IPSEC_API_SAD_COMBO_12 = 12,
+  IPSEC_API_SAD_COMBO_20 = 20,
 };
 
 enum ipsec_proto
@@ -261,6 +265,7 @@
     @param integrity_key - integrity keying material
     @param tunnel_src_address - IPsec tunnel source address IPv6 if is_tunnel_ipv6 is non-zero, else IPv4. Only valid if is_tunnel is non-zero
     @param tunnel_dst_address - IPsec tunnel destination address IPv6 if is_tunnel_ipv6 is non-zero, else IPv4. Only valid if is_tunnel is non-zero
+    @param tx_table_id - the FIB id used for encapsulated packets
  */
 typedef ipsec_sad_entry
 {
@@ -280,6 +285,7 @@
 
   vl_api_address_t tunnel_src;
   vl_api_address_t tunnel_dst;
+  u32 tx_table_id;
 };
 
 /** \brief IPsec: Add/delete Security Association Database entry
@@ -687,41 +693,18 @@
     @param replay_window - bit map of seq nums received relative to last_seq if using anti-replay
     @param total_data_size - total bytes sent or received
     @param udp_encap - 1 if UDP encap enabled, 0 otherwise
-    @param tx_table_id - the FIB id used for encapsulated packets
 */
 define ipsec_sa_details {
   u32 context;
-  u32 sa_id;
+  vl_api_ipsec_sad_entry_t entry;
+
   u32 sw_if_index;
-
-  u32 spi;
-  u8 protocol;
-
-  u8 crypto_alg;
-  u8 crypto_key_len;
-  u8 crypto_key[128];
-
-  u8 integ_alg;
-  u8 integ_key_len;
-  u8 integ_key[128];
-
-  u8 use_esn;
-  u8 use_anti_replay;
-
-  u8 is_tunnel;
-  u8 is_tunnel_ip6;
-  u8 tunnel_src_addr[16];
-  u8 tunnel_dst_addr[16];
-
   u32 salt;
   u64 seq_outbound;
   u64 last_seq_inbound;
   u64 replay_window;
 
   u64 total_data_size;
-  u8 udp_encap;
-
-  u32 tx_table_id;
 };
 
 /** \brief Set key on IPsec interface
diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c
index 06f9546..2d464b3 100644
--- a/src/vnet/ipsec/ipsec_api.c
+++ b/src/vnet/ipsec/ipsec_api.c
@@ -217,6 +217,19 @@
   return (VNET_API_ERROR_UNIMPLEMENTED);
 }
 
+static vl_api_ipsec_proto_t
+ipsec_proto_encode (ipsec_protocol_t p)
+{
+  switch (p)
+    {
+    case IPSEC_PROTOCOL_ESP:
+      return clib_host_to_net_u32 (IPSEC_API_PROTO_ESP);
+    case IPSEC_PROTOCOL_AH:
+      return clib_host_to_net_u32 (IPSEC_API_PROTO_AH);
+    }
+  return (VNET_API_ERROR_UNIMPLEMENTED);
+}
+
 static int
 ipsec_crypto_algo_decode (vl_api_ipsec_crypto_alg_t in,
 			  ipsec_crypto_alg_t * out)
@@ -234,6 +247,23 @@
   return (VNET_API_ERROR_UNIMPLEMENTED);
 }
 
+static vl_api_ipsec_crypto_alg_t
+ipsec_crypto_algo_encode (ipsec_crypto_alg_t c)
+{
+  switch (c)
+    {
+#define _(v,f,s) case IPSEC_CRYPTO_ALG_##f:                     \
+      return clib_host_to_net_u32(IPSEC_API_CRYPTO_ALG_##f);
+      foreach_ipsec_crypto_alg
+#undef _
+    case IPSEC_CRYPTO_N_ALG:
+      break;
+    }
+  ASSERT (0);
+  return (VNET_API_ERROR_UNIMPLEMENTED);
+}
+
+
 static int
 ipsec_integ_algo_decode (vl_api_ipsec_integ_alg_t in, ipsec_integ_alg_t * out)
 {
@@ -250,86 +280,122 @@
   return (VNET_API_ERROR_UNIMPLEMENTED);
 }
 
-static void
-vl_api_ipsec_key_decode (const vl_api_key_t * key, u8 * len, u8 out[128])
+static vl_api_ipsec_integ_alg_t
+ipsec_integ_algo_encode (ipsec_integ_alg_t i)
 {
-  *len = key->length;
-  clib_memcpy (out, key->data, key->length);
+  switch (i)
+    {
+#define _(v,f,s) case IPSEC_INTEG_ALG_##f:                      \
+      return (clib_host_to_net_u32(IPSEC_API_INTEG_ALG_##f));
+      foreach_ipsec_integ_alg
+#undef _
+    case IPSEC_INTEG_N_ALG:
+      break;
+    }
+  ASSERT (0);
+  return (VNET_API_ERROR_UNIMPLEMENTED);
 }
 
 static void
-vl_api_ipsec_sad_flags_decode (vl_api_ipsec_sad_flags_t in, ipsec_sa_t * sa)
+ipsec_key_decode (const vl_api_key_t * key, ipsec_key_t * out)
 {
+  ipsec_mk_key (out, key->data, key->length);
+}
+
+static void
+ipsec_key_encode (const ipsec_key_t * in, vl_api_key_t * out)
+{
+  out->length = in->len;
+  clib_memcpy (out->data, in->data, out->length);
+}
+
+static ipsec_sa_flags_t
+ipsec_sa_flags_decode (vl_api_ipsec_sad_flags_t in)
+{
+  ipsec_sa_flags_t flags = IPSEC_SA_FLAG_NONE;
   in = clib_net_to_host_u32 (in);
 
-  if (in & IPSEC_API_SAD_FLAG_USE_EXTENDED_SEQ_NUM)
-    sa->use_esn = 1;
-  if (in & IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY)
-    sa->use_anti_replay = 1;
-  if (in & IPSEC_API_SAD_FLAG_IS_TUNNEL)
-    sa->is_tunnel = 1;
-  if (in & IPSEC_API_SAD_FLAG_IS_TUNNEL_V6)
-    sa->is_tunnel_ip6 = 1;
-  if (in & IPSEC_API_SAD_FLAG_UDP_ENCAP)
-    sa->udp_encap = 1;
+#define _(v,f,s) if (in & IPSEC_API_SAD_FLAG_##f)    \
+    flags |= IPSEC_SA_FLAG_##f;
+  foreach_ipsec_sa_flags
+#undef _
+    return (flags);
 }
 
+static vl_api_ipsec_sad_flags_t
+ipsec_sad_flags_encode (const ipsec_sa_t * sa)
+{
+  vl_api_ipsec_sad_flags_t flags = IPSEC_API_SAD_FLAG_NONE;
+
+  if (sa->use_esn)
+    flags |= IPSEC_API_SAD_FLAG_USE_EXTENDED_SEQ_NUM;
+  if (sa->use_anti_replay)
+    flags |= IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY;
+  if (sa->is_tunnel)
+    flags |= IPSEC_API_SAD_FLAG_IS_TUNNEL;
+  if (sa->is_tunnel_ip6)
+    flags |= IPSEC_API_SAD_FLAG_IS_TUNNEL_V6;
+  if (sa->udp_encap)
+    flags |= IPSEC_API_SAD_FLAG_UDP_ENCAP;
+
+  return clib_host_to_net_u32 (flags);
+}
 
 static void vl_api_ipsec_sad_entry_add_del_t_handler
   (vl_api_ipsec_sad_entry_add_del_t * mp)
 {
   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
   vl_api_ipsec_sad_entry_add_del_reply_t *rmp;
+  ip46_address_t tun_src = { }, tun_dst =
+  {
+  };
+  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;
   int rv;
+
 #if WITH_LIBSSL > 0
-  ipsec_main_t *im = &ipsec_main;
-  ipsec_sa_t sa;
 
-  clib_memset (&sa, 0, sizeof (sa));
+  id = ntohl (mp->entry.sad_id);
+  spi = ntohl (mp->entry.spi);
 
-  sa.id = ntohl (mp->entry.sad_id);
-  sa.spi = ntohl (mp->entry.spi);
-
-  rv = ipsec_proto_decode (mp->entry.protocol, &sa.protocol);
+  rv = ipsec_proto_decode (mp->entry.protocol, &proto);
 
   if (rv)
     goto out;
 
-  rv = ipsec_crypto_algo_decode (mp->entry.crypto_algorithm, &sa.crypto_alg);
+  rv = ipsec_crypto_algo_decode (mp->entry.crypto_algorithm, &crypto_alg);
 
   if (rv)
     goto out;
 
-  rv = ipsec_integ_algo_decode (mp->entry.integrity_algorithm, &sa.integ_alg);
+  rv = ipsec_integ_algo_decode (mp->entry.integrity_algorithm, &integ_alg);
 
   if (rv)
     goto out;
 
-  vl_api_ipsec_key_decode (&mp->entry.crypto_key,
-			   &sa.crypto_key_len, sa.crypto_key);
-  vl_api_ipsec_key_decode (&mp->entry.integrity_key,
-			   &sa.integ_key_len, sa.integ_key);
+  ipsec_key_decode (&mp->entry.crypto_key, &crypto_key);
+  ipsec_key_decode (&mp->entry.integrity_key, &integ_key);
 
-  vl_api_ipsec_sad_flags_decode (mp->entry.flags, &sa);
+  flags = ipsec_sa_flags_decode (mp->entry.flags);
 
-  if (sa.is_tunnel_ip6 || sa.is_tunnel)
-    {
-      ip_address_decode (&mp->entry.tunnel_src, &sa.tunnel_src_addr);
-      ip_address_decode (&mp->entry.tunnel_dst, &sa.tunnel_dst_addr);
-    }
+  ip_address_decode (&mp->entry.tunnel_src, &tun_src);
+  ip_address_decode (&mp->entry.tunnel_dst, &tun_dst);
 
-  clib_error_t *err = ipsec_check_support_cb (im, &sa);
-  if (err)
-    {
-      clib_warning ("%s", err->what);
-      rv = VNET_API_ERROR_UNIMPLEMENTED;
-      goto out;
-    }
 
-  rv = ipsec_add_del_sa (vm, &sa, mp->is_add);
+  if (mp->is_add)
+    rv = ipsec_sa_add (id, spi, proto,
+		       crypto_alg, &crypto_key,
+		       integ_alg, &integ_key, flags,
+		       0, &tun_src, &tun_dst, NULL);
+  else
+    rv = ipsec_sa_del (id);
+
 #else
   rv = VNET_API_ERROR_UNIMPLEMENTED;
-  goto out;
 #endif
 
 out:
@@ -525,17 +591,17 @@
 {
   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
   vl_api_ipsec_sa_set_key_reply_t *rmp;
+  ipsec_key_t ck, ik;
+  u32 id;
   int rv;
 #if WITH_LIBSSL > 0
-  ipsec_sa_t sa;
-  sa.id = ntohl (mp->sa_id);
 
-  vl_api_ipsec_key_decode (&mp->crypto_key,
-			   &sa.crypto_key_len, sa.crypto_key);
-  vl_api_ipsec_key_decode (&mp->integrity_key,
-			   &sa.integ_key_len, sa.integ_key);
+  id = ntohl (mp->sa_id);
 
-  rv = ipsec_set_sa_key (vm, &sa);
+  ipsec_key_decode (&mp->crypto_key, &ck);
+  ipsec_key_decode (&mp->integrity_key, &ik);
+
+  rv = ipsec_set_sa_key (id, &ck, &ik);
 #else
   rv = VNET_API_ERROR_UNIMPLEMENTED;
 #endif
@@ -571,8 +637,8 @@
   tun.remote_integ_key_len = mp->remote_integ_key_len;
   tun.udp_encap = mp->udp_encap;
   tun.tx_table_id = ntohl (mp->tx_table_id);
-  memcpy (&tun.local_ip, mp->local_ip, 4);
-  memcpy (&tun.remote_ip, mp->remote_ip, 4);
+  memcpy (&tun.local_ip.ip4, mp->local_ip, 4);
+  memcpy (&tun.remote_ip.ip4, mp->remote_ip, 4);
   memcpy (&tun.local_crypto_key, &mp->local_crypto_key,
 	  mp->local_crypto_key_len);
   memcpy (&tun.remote_crypto_key, &mp->remote_crypto_key,
@@ -590,11 +656,12 @@
   rv = VNET_API_ERROR_UNIMPLEMENTED;
 #endif
 
-  REPLY_MACRO2 (VL_API_IPSEC_TUNNEL_IF_ADD_DEL_REPLY, (
-							{
-							rmp->sw_if_index =
-							htonl (sw_if_index);
-							}));
+  /* *INDENT-OFF* */
+  REPLY_MACRO2 (VL_API_IPSEC_TUNNEL_IF_ADD_DEL_REPLY,
+  ({
+    rmp->sw_if_index = htonl (sw_if_index);
+  }));
+  /* *INDENT-ON* */
 }
 
 static void
@@ -608,40 +675,29 @@
   mp->_vl_msg_id = ntohs (VL_API_IPSEC_SA_DETAILS);
   mp->context = context;
 
-  mp->sa_id = htonl (sa->id);
-  mp->sw_if_index = htonl (sw_if_index);
+  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 (fib_table_get_table_id (sa->tx_fib_index, FIB_PROTOCOL_IP4));
 
-  mp->spi = htonl (sa->spi);
-  mp->protocol = sa->protocol;
+  mp->entry.crypto_algorithm = ipsec_crypto_algo_encode (sa->crypto_alg);
+  ipsec_key_encode (&sa->crypto_key, &mp->entry.crypto_key);
 
-  mp->crypto_alg = sa->crypto_alg;
-  mp->crypto_key_len = sa->crypto_key_len;
-  memcpy (mp->crypto_key, sa->crypto_key, sa->crypto_key_len);
+  mp->entry.integrity_algorithm = ipsec_integ_algo_encode (sa->integ_alg);
+  ipsec_key_encode (&sa->integ_key, &mp->entry.integrity_key);
 
-  mp->integ_alg = sa->integ_alg;
-  mp->integ_key_len = sa->integ_key_len;
-  memcpy (mp->integ_key, sa->integ_key, sa->integ_key_len);
-
-  mp->use_esn = sa->use_esn;
-  mp->use_anti_replay = sa->use_anti_replay;
-
-  mp->is_tunnel = sa->is_tunnel;
-  mp->is_tunnel_ip6 = sa->is_tunnel_ip6;
+  mp->entry.flags = ipsec_sad_flags_encode (sa);
 
   if (sa->is_tunnel)
     {
-      if (sa->is_tunnel_ip6)
-	{
-	  memcpy (mp->tunnel_src_addr, &sa->tunnel_src_addr.ip6, 16);
-	  memcpy (mp->tunnel_dst_addr, &sa->tunnel_dst_addr.ip6, 16);
-	}
-      else
-	{
-	  memcpy (mp->tunnel_src_addr, &sa->tunnel_src_addr.ip4, 4);
-	  memcpy (mp->tunnel_dst_addr, &sa->tunnel_dst_addr.ip4, 4);
-	}
+      ip_address_encode (&sa->tunnel_src_addr, IP46_TYPE_ANY,
+			 &mp->entry.tunnel_src);
+      ip_address_encode (&sa->tunnel_dst_addr, IP46_TYPE_ANY,
+			 &mp->entry.tunnel_dst);
     }
 
+  mp->sw_if_index = htonl (sw_if_index);
   mp->salt = clib_host_to_net_u32 (sa->salt);
   mp->seq_outbound = clib_host_to_net_u64 (((u64) sa->seq));
   mp->last_seq_inbound = clib_host_to_net_u64 (((u64) sa->last_seq));
@@ -653,10 +709,6 @@
   if (sa->use_anti_replay)
     mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
   mp->total_data_size = clib_host_to_net_u64 (sa->total_data_size);
-  mp->udp_encap = sa->udp_encap;
-
-  mp->tx_table_id =
-    htonl (fib_table_get_table_id (sa->tx_fib_index, FIB_PROTOCOL_IP4));
 
   vl_api_send_msg (reg, (u8 *) mp);
 }
diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c
index 4bc1437..f66bf6d 100644
--- a/src/vnet/ipsec/ipsec_cli.c
+++ b/src/vnet/ipsec/ipsec_cli.c
@@ -73,87 +73,62 @@
 			     unformat_input_t * input,
 			     vlib_cli_command_t * cmd)
 {
-  ipsec_main_t *im = &ipsec_main;
   unformat_input_t _line_input, *line_input = &_line_input;
-  ipsec_sa_t sa;
-  int is_add = ~0;
-  u8 *ck = 0, *ik = 0;
-  clib_error_t *error = NULL;
+  ip46_address_t tun_src = { }, tun_dst =
+  {
+  };
+  ipsec_crypto_alg_t crypto_alg;
+  ipsec_integ_alg_t integ_alg;
+  ipsec_protocol_t proto;
+  ipsec_sa_flags_t flags;
+  clib_error_t *error;
+  ipsec_key_t ck, ik;
+  int is_add, rv;
+  u32 id, spi;
 
-  clib_memset (&sa, 0, sizeof (sa));
-  sa.tx_fib_index = ~((u32) 0);	/* Only supported for ipsec interfaces */
+  error = NULL;
+  is_add = 0;
+  flags = IPSEC_SA_FLAG_NONE;
+  proto = IPSEC_PROTOCOL_ESP;
 
   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, "add %u", &sa.id))
+      if (unformat (line_input, "add %u", &id))
 	is_add = 1;
-      else if (unformat (line_input, "del %u", &sa.id))
+      else if (unformat (line_input, "del %u", &id))
 	is_add = 0;
-      else if (unformat (line_input, "spi %u", &sa.spi))
+      else if (unformat (line_input, "spi %u", &spi))
 	;
       else if (unformat (line_input, "esp"))
-	sa.protocol = IPSEC_PROTOCOL_ESP;
+	proto = IPSEC_PROTOCOL_ESP;
       else if (unformat (line_input, "ah"))
-	{
-	  sa.protocol = IPSEC_PROTOCOL_AH;
-	}
-      else
-	if (unformat (line_input, "crypto-key %U", unformat_hex_string, &ck))
-	sa.crypto_key_len = vec_len (ck);
-      else
-	if (unformat
-	    (line_input, "crypto-alg %U", unformat_ipsec_crypto_alg,
-	     &sa.crypto_alg))
-	{
-	  if (sa.crypto_alg < IPSEC_CRYPTO_ALG_NONE ||
-	      sa.crypto_alg >= IPSEC_CRYPTO_N_ALG)
-	    {
-	      error = clib_error_return (0, "unsupported crypto-alg: '%U'",
-					 format_ipsec_crypto_alg,
-					 sa.crypto_alg);
-	      goto done;
-	    }
-	}
-      else
-	if (unformat (line_input, "integ-key %U", unformat_hex_string, &ik))
-	sa.integ_key_len = vec_len (ik);
-      else if (unformat (line_input, "integ-alg %U", unformat_ipsec_integ_alg,
-			 &sa.integ_alg))
-	{
-	  if (sa.integ_alg < IPSEC_INTEG_ALG_NONE ||
-	      sa.integ_alg >= IPSEC_INTEG_N_ALG)
-	    {
-	      error = clib_error_return (0, "unsupported integ-alg: '%U'",
-					 format_ipsec_integ_alg,
-					 sa.integ_alg);
-	      goto done;
-	    }
-	}
+	proto = IPSEC_PROTOCOL_AH;
+      else if (unformat (line_input, "crypto-key %U",
+			 unformat_ipsec_key, &ck))
+	;
+      else if (unformat (line_input, "crypto-alg %U",
+			 unformat_ipsec_crypto_alg, &crypto_alg))
+	;
+      else if (unformat (line_input, "integ-key %U", unformat_ipsec_key, &ik))
+	;
+      else if (unformat (line_input, "integ-alg %U",
+			 unformat_ipsec_integ_alg, &integ_alg))
+	;
       else if (unformat (line_input, "tunnel-src %U",
-			 unformat_ip4_address, &sa.tunnel_src_addr.ip4))
-	sa.is_tunnel = 1;
-      else if (unformat (line_input, "tunnel-dst %U",
-			 unformat_ip4_address, &sa.tunnel_dst_addr.ip4))
-	sa.is_tunnel = 1;
-      else if (unformat (line_input, "tunnel-src %U",
-			 unformat_ip6_address, &sa.tunnel_src_addr.ip6))
+			 unformat_ip46_address, &tun_src))
 	{
-	  sa.is_tunnel = 1;
-	  sa.is_tunnel_ip6 = 1;
+	  flags |= IPSEC_SA_FLAG_IS_TUNNEL;
+	  if (!ip46_address_is_ip4 (&tun_src))
+	    flags |= IPSEC_SA_FLAG_IS_TUNNEL_V6;
 	}
       else if (unformat (line_input, "tunnel-dst %U",
-			 unformat_ip6_address, &sa.tunnel_dst_addr.ip6))
-	{
-	  sa.is_tunnel = 1;
-	  sa.is_tunnel_ip6 = 1;
-	}
+			 unformat_ip46_address, &tun_dst))
+	;
       else if (unformat (line_input, "udp-encap"))
-	{
-	  sa.udp_encap = 1;
-	}
+	flags |= IPSEC_SA_FLAG_UDP_ENCAP;
       else
 	{
 	  error = clib_error_return (0, "parse error: '%U'",
@@ -162,26 +137,15 @@
 	}
     }
 
-  if (sa.crypto_key_len > sizeof (sa.crypto_key))
-    sa.crypto_key_len = sizeof (sa.crypto_key);
-
-  if (sa.integ_key_len > sizeof (sa.integ_key))
-    sa.integ_key_len = sizeof (sa.integ_key);
-
-  if (ck)
-    memcpy (sa.crypto_key, ck, sa.crypto_key_len);
-
-  if (ik)
-    memcpy (sa.integ_key, ik, sa.integ_key_len);
-
   if (is_add)
-    {
-      error = ipsec_check_support_cb (im, &sa);
-      if (error)
-	goto done;
-    }
+    rv = ipsec_sa_add (id, spi, proto, crypto_alg,
+		       &ck, integ_alg, &ik, flags,
+		       0, &tun_src, &tun_dst, NULL);
+  else
+    rv = ipsec_sa_del (id);
 
-  ipsec_add_del_sa (vm, &sa, is_add);
+  if (rv)
+    clib_error_return (0, "failed");
 
 done:
   unformat_free (line_input);
@@ -388,25 +352,22 @@
 			     vlib_cli_command_t * cmd)
 {
   unformat_input_t _line_input, *line_input = &_line_input;
-  ipsec_sa_t sa;
-  u8 *ck = 0, *ik = 0;
   clib_error_t *error = NULL;
-
-  clib_memset (&sa, 0, sizeof (sa));
+  ipsec_key_t ck, ik;
+  u32 id;
 
   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, "%u", &sa.id))
+      if (unformat (line_input, "%u", &id))
 	;
       else
-	if (unformat (line_input, "crypto-key %U", unformat_hex_string, &ck))
-	sa.crypto_key_len = vec_len (ck);
-      else
-	if (unformat (line_input, "integ-key %U", unformat_hex_string, &ik))
-	sa.integ_key_len = vec_len (ik);
+	if (unformat (line_input, "crypto-key %U", unformat_ipsec_key, &ck))
+	;
+      else if (unformat (line_input, "integ-key %U", unformat_ipsec_key, &ik))
+	;
       else
 	{
 	  error = clib_error_return (0, "parse error: '%U'",
@@ -415,19 +376,7 @@
 	}
     }
 
-  if (sa.crypto_key_len > sizeof (sa.crypto_key))
-    sa.crypto_key_len = sizeof (sa.crypto_key);
-
-  if (sa.integ_key_len > sizeof (sa.integ_key))
-    sa.integ_key_len = sizeof (sa.integ_key);
-
-  if (ck)
-    strncpy ((char *) sa.crypto_key, (char *) ck, sa.crypto_key_len);
-
-  if (ik)
-    strncpy ((char *) sa.integ_key, (char *) ik, sa.integ_key_len);
-
-  ipsec_set_sa_key (vm, &sa);
+  ipsec_set_sa_key (id, &ck, &ik);
 
 done:
   unformat_free (line_input);
@@ -448,96 +397,45 @@
 show_ipsec_command_fn (vlib_main_t * vm,
 		       unformat_input_t * input, vlib_cli_command_t * cmd)
 {
-  ipsec_sa_t *sa;
   ipsec_main_t *im = &ipsec_main;
-  u32 i;
-  ipsec_tunnel_if_t *t;
+  u32 spd_id, sw_if_index, sai;
   vnet_hw_interface_t *hi;
+  ipsec_tunnel_if_t *t;
   u8 *protocol = NULL;
   u8 *policy = NULL;
-  u32 tx_table_id, spd_id, sw_if_index;
+  u32 i;
 
   /* *INDENT-OFF* */
-  pool_foreach (sa, im->sad, ({
-    if (sa->id) {
-      vlib_cli_output(vm, "sa %u spi %u mode %s protocol %s%s%s%s", sa->id, sa->spi,
-                      sa->is_tunnel ? "tunnel" : "transport",
-                      sa->protocol ? "esp" : "ah",
-		      sa->udp_encap ? " udp-encap-enabled" : "",
-                      sa->use_anti_replay ? " anti-replay" : "",
-                      sa->use_esn ? " extended-sequence-number" : "");
-      if (sa->protocol == IPSEC_PROTOCOL_ESP) {
-        vlib_cli_output(vm, "  crypto alg %U%s%U integrity alg %U%s%U",
-                        format_ipsec_crypto_alg, sa->crypto_alg,
-                        sa->crypto_alg ? " key " : "",
-                        format_hex_bytes, sa->crypto_key, sa->crypto_key_len,
-                        format_ipsec_integ_alg, sa->integ_alg,
-                        sa->integ_alg ? " key " : "",
-                        format_hex_bytes, sa->integ_key, sa->integ_key_len);
-      }
-      if (sa->is_tunnel && sa->is_tunnel_ip6) {
-        vlib_cli_output(vm, "  tunnel src %U dst %U",
-                        format_ip6_address, &sa->tunnel_src_addr.ip6,
-                        format_ip6_address, &sa->tunnel_dst_addr.ip6);
-      } else if (sa->is_tunnel) {
-        vlib_cli_output(vm, "  tunnel src %U dst %U",
-                        format_ip4_address, &sa->tunnel_src_addr.ip4,
-                        format_ip4_address, &sa->tunnel_dst_addr.ip4);
-      }
-    }
+  pool_foreach_index (sai, im->sad, ({
+     vlib_cli_output(vm, "%U", format_ipsec_sa, sai);
   }));
-  /* *INDENT-ON* */
-
-  /* *INDENT-OFF* */
   pool_foreach_index (i, im->spds, ({
     vlib_cli_output(vm, "%U", format_ipsec_spd, i);
   }));
-  /* *INDENT-ON* */
 
   vlib_cli_output (vm, "SPD Bindings:");
-  /* *INDENT-OFF* */
+
   hash_foreach(sw_if_index, spd_id, im->spd_index_by_sw_if_index, ({
-        vlib_cli_output (vm, "  %d -> %U", spd_id,
-                         format_vnet_sw_if_index_name, im->vnet_main,
-                         sw_if_index);
+    vlib_cli_output (vm, "  %d -> %U", spd_id,
+                     format_vnet_sw_if_index_name, im->vnet_main,
+                     sw_if_index);
   }));
   /* *INDENT-ON* */
 
-
   vlib_cli_output (vm, "tunnel interfaces");
   /* *INDENT-OFF* */
   pool_foreach (t, im->tunnel_interfaces, ({
     if (t->hw_if_index == ~0)
       continue;
     hi = vnet_get_hw_interface (im->vnet_main, t->hw_if_index);
-    vlib_cli_output(vm, "  %s seq", hi->name);
-    sa = pool_elt_at_index(im->sad, t->output_sa_index);
 
-    tx_table_id = fib_table_get_table_id(sa->tx_fib_index, FIB_PROTOCOL_IP4);
+    vlib_cli_output(vm, "  %s", hi->name);
 
-    vlib_cli_output(vm, "   seq %u seq-hi %u esn %u anti-replay %u udp-encap %u tx-table %u",
-                    sa->seq, sa->seq_hi, sa->use_esn, sa->use_anti_replay, sa->udp_encap, tx_table_id);
-    vlib_cli_output(vm, "   local-spi %u local-ip %U", sa->spi,
-                    format_ip4_address, &sa->tunnel_src_addr.ip4);
-    vlib_cli_output(vm, "   local-crypto %U %U",
-                    format_ipsec_crypto_alg, sa->crypto_alg,
-                    format_hex_bytes, sa->crypto_key, sa->crypto_key_len);
-    vlib_cli_output(vm, "   local-integrity %U %U",
-                    format_ipsec_integ_alg, sa->integ_alg,
-                    format_hex_bytes, sa->integ_key, sa->integ_key_len);
-    sa = pool_elt_at_index(im->sad, t->input_sa_index);
-    vlib_cli_output(vm, "   last-seq %u last-seq-hi %u esn %u anti-replay %u window %U",
-                    sa->last_seq, sa->last_seq_hi, sa->use_esn,
-                    sa->use_anti_replay,
-                    format_ipsec_replay_window, sa->replay_window);
-    vlib_cli_output(vm, "   remote-spi %u remote-ip %U", sa->spi,
-                    format_ip4_address, &sa->tunnel_src_addr.ip4);
-    vlib_cli_output(vm, "   remote-crypto %U %U",
-                    format_ipsec_crypto_alg, sa->crypto_alg,
-                    format_hex_bytes, sa->crypto_key, sa->crypto_key_len);
-    vlib_cli_output(vm, "   remote-integrity %U %U",
-                    format_ipsec_integ_alg, sa->integ_alg,
-                    format_hex_bytes, sa->integ_key, sa->integ_key_len);
+    vlib_cli_output(vm, "  out-bound sa");
+    vlib_cli_output(vm, "   %U", format_ipsec_sa, t->output_sa_index);
+
+    vlib_cli_output(vm, "  in-bound sa");
+    vlib_cli_output(vm, "   %U", format_ipsec_sa, t->input_sa_index);
   }));
   vec_free(policy);
   vec_free(protocol);
diff --git a/src/vnet/ipsec/ipsec_format.c b/src/vnet/ipsec/ipsec_format.c
index cbd6723..04a2a0b 100644
--- a/src/vnet/ipsec/ipsec_format.c
+++ b/src/vnet/ipsec/ipsec_format.c
@@ -19,6 +19,7 @@
 #include <vnet/api_errno.h>
 #include <vnet/ip/ip.h>
 #include <vnet/interface.h>
+#include <vnet/fib/fib_table.h>
 
 #include <vnet/ipsec/ipsec.h>
 
@@ -208,6 +209,77 @@
   return (s);
 }
 
+u8 *
+format_ipsec_key (u8 * s, va_list * args)
+{
+  ipsec_key_t *key = va_arg (*args, ipsec_key_t *);
+
+  return (format (s, "%U", format_hex_bytes, key->data, key->len));
+}
+
+uword
+unformat_ipsec_key (unformat_input_t * input, va_list * args)
+{
+  ipsec_key_t *key = va_arg (*args, ipsec_key_t *);
+  u8 *data;
+
+  if (unformat (input, "%U", unformat_hex_string, &data))
+    {
+      ipsec_mk_key (key, data, vec_len (data));
+      vec_free (data);
+    }
+  else
+    return 0;
+  return 1;
+}
+
+u8 *
+format_ipsec_sa (u8 * s, va_list * args)
+{
+  u32 sai = va_arg (*args, u32);
+  ipsec_main_t *im = &ipsec_main;
+  u32 tx_table_id;
+  ipsec_sa_t *sa;
+
+  sa = pool_elt_at_index (im->sad, sai);
+
+  s = format (s, "[%d] sa %u spi %u mode %s%s protocol %s%s%s%s",
+	      sai, sa->id, sa->spi,
+	      sa->is_tunnel ? "tunnel" : "transport",
+	      sa->is_tunnel_ip6 ? "-ip6" : "",
+	      sa->protocol ? "esp" : "ah",
+	      sa->udp_encap ? " udp-encap-enabled" : "",
+	      sa->use_anti_replay ? " anti-replay" : "",
+	      sa->use_esn ? " extended-sequence-number" : "");
+  s = format (s, "\n   last-seq %u last-seq-hi %u  window %U",
+	      sa->last_seq, sa->last_seq_hi,
+	      format_ipsec_replay_window, sa->replay_window);
+  s = format (s, "\n   crypto alg %U%s%U",
+	      format_ipsec_crypto_alg, sa->crypto_alg,
+	      sa->crypto_alg ? " key " : "",
+	      format_ipsec_key, &sa->crypto_key);
+  s = format (s, "\n   integrity alg %U%s%U",
+	      format_ipsec_integ_alg, sa->integ_alg,
+	      sa->integ_alg ? " key " : "", format_ipsec_key, &sa->integ_key);
+
+  if (sa->is_tunnel)
+    {
+      tx_table_id = fib_table_get_table_id (sa->tx_fib_index,
+					    FIB_PROTOCOL_IP4);
+      s = format (s, "\n   table-ID %d tunnel src %U dst %U",
+		  tx_table_id,
+		  format_ip46_address, &sa->tunnel_src_addr, IP46_TYPE_ANY,
+		  format_ip46_address, &sa->tunnel_dst_addr, IP46_TYPE_ANY);
+      s = format (s, "\n    resovle via fib-entry: %d", sa->fib_entry_index);
+      s = format (s, "\n    stacked on:");
+      s =
+	format (s, "\n      %U", format_dpo_id, &sa->dpo[IPSEC_PROTOCOL_ESP],
+		6);
+    }
+
+  return (s);
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/src/vnet/ipsec/ipsec_if.c b/src/vnet/ipsec/ipsec_if.c
index 0dfb690..357d638 100644
--- a/src/vnet/ipsec/ipsec_if.c
+++ b/src/vnet/ipsec/ipsec_if.c
@@ -250,6 +250,18 @@
   return 0;
 }
 
+static u32
+ipsec_tun_mk_input_sa_id (u32 ti)
+{
+  return (0x80000000 | ti);
+}
+
+static u32
+ipsec_tun_mk_output_sa_id (u32 ti)
+{
+  return (0xc0000000 | ti);
+}
+
 int
 ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm,
 				  ipsec_add_del_tunnel_args_t * args,
@@ -260,12 +272,13 @@
   vnet_hw_interface_t *hi = NULL;
   u32 hw_if_index = ~0;
   uword *p;
-  ipsec_sa_t *sa;
   u32 dev_instance;
   u32 slot;
-  u32 tx_fib_index = ~0;
+  ipsec_key_t crypto_key, integ_key;
+  ipsec_sa_flags_t flags;
+  int rv;
 
-  u64 key = (u64) args->remote_ip.as_u32 << 32 | (u64) args->remote_spi;
+  u64 key = (u64) args->remote_ip.ip4.as_u32 << 32 | (u64) args->remote_spi;
   p = hash_get (im->ipsec_if_pool_index_by_key, key);
 
   if (args->is_add)
@@ -274,10 +287,6 @@
       if (p)
 	return VNET_API_ERROR_INVALID_VALUE;
 
-      tx_fib_index = fib_table_find (FIB_PROTOCOL_IP4, args->tx_table_id);
-      if (tx_fib_index == ~((u32) 0))
-	return VNET_API_ERROR_NO_SUCH_FIB;
-
       pool_get_aligned (im->tunnel_interfaces, t, CLIB_CACHE_LINE_BYTES);
       clib_memset (t, 0, sizeof (*t));
 
@@ -296,59 +305,53 @@
       hash_set (im->ipsec_if_real_dev_by_show_dev, t->show_instance,
 		dev_instance);
 
-      pool_get (im->sad, sa);
-      clib_memset (sa, 0, sizeof (*sa));
-      t->input_sa_index = sa - im->sad;
-      sa->protocol = IPSEC_PROTOCOL_ESP;
-      sa->spi = args->remote_spi;
-      sa->tunnel_src_addr.ip4.as_u32 = args->remote_ip.as_u32;
-      sa->tunnel_dst_addr.ip4.as_u32 = args->local_ip.as_u32;
-      sa->is_tunnel = 1;
-      sa->use_esn = args->esn;
-      sa->use_anti_replay = args->anti_replay;
-      sa->integ_alg = args->integ_alg;
-      sa->udp_encap = args->udp_encap;
-      sa->tx_fib_index = ~((u32) 0);	/* Not used, but set for troubleshooting */
-      if (args->remote_integ_key_len <= sizeof (args->remote_integ_key))
-	{
-	  sa->integ_key_len = args->remote_integ_key_len;
-	  clib_memcpy (sa->integ_key, args->remote_integ_key,
-		       args->remote_integ_key_len);
-	}
-      sa->crypto_alg = args->crypto_alg;
-      if (args->remote_crypto_key_len <= sizeof (args->remote_crypto_key))
-	{
-	  sa->crypto_key_len = args->remote_crypto_key_len;
-	  clib_memcpy (sa->crypto_key, args->remote_crypto_key,
-		       args->remote_crypto_key_len);
-	}
+      flags = IPSEC_SA_FLAG_IS_TUNNEL;
+      if (args->udp_encap)
+	flags |= IPSEC_SA_FLAG_UDP_ENCAP;
+      if (args->esn)
+	flags |= IPSEC_SA_FLAG_USE_EXTENDED_SEQ_NUM;
+      if (args->anti_replay)
+	flags |= IPSEC_SA_FLAG_USE_ANTI_REPLAY;
 
-      pool_get (im->sad, sa);
-      clib_memset (sa, 0, sizeof (*sa));
-      t->output_sa_index = sa - im->sad;
-      sa->protocol = IPSEC_PROTOCOL_ESP;
-      sa->spi = args->local_spi;
-      sa->tunnel_src_addr.ip4.as_u32 = args->local_ip.as_u32;
-      sa->tunnel_dst_addr.ip4.as_u32 = args->remote_ip.as_u32;
-      sa->is_tunnel = 1;
-      sa->use_esn = args->esn;
-      sa->use_anti_replay = args->anti_replay;
-      sa->integ_alg = args->integ_alg;
-      sa->udp_encap = args->udp_encap;
-      sa->tx_fib_index = tx_fib_index;
-      if (args->local_integ_key_len <= sizeof (args->local_integ_key))
-	{
-	  sa->integ_key_len = args->local_integ_key_len;
-	  clib_memcpy (sa->integ_key, args->local_integ_key,
-		       args->local_integ_key_len);
-	}
-      sa->crypto_alg = args->crypto_alg;
-      if (args->local_crypto_key_len <= sizeof (args->local_crypto_key))
-	{
-	  sa->crypto_key_len = args->local_crypto_key_len;
-	  clib_memcpy (sa->crypto_key, args->local_crypto_key,
-		       args->local_crypto_key_len);
-	}
+      ipsec_mk_key (&crypto_key,
+		    args->remote_crypto_key, args->remote_crypto_key_len);
+      ipsec_mk_key (&integ_key,
+		    args->remote_integ_key, args->remote_integ_key_len);
+
+      rv = ipsec_sa_add (ipsec_tun_mk_input_sa_id (dev_instance),
+			 args->remote_spi,
+			 IPSEC_PROTOCOL_ESP,
+			 args->crypto_alg,
+			 &crypto_key,
+			 args->integ_alg,
+			 &integ_key,
+			 flags,
+			 args->tx_table_id,
+			 &args->remote_ip,
+			 &args->local_ip, &t->input_sa_index);
+
+      if (rv)
+	return VNET_API_ERROR_UNIMPLEMENTED;
+
+      ipsec_mk_key (&crypto_key,
+		    args->local_crypto_key, args->local_crypto_key_len);
+      ipsec_mk_key (&integ_key,
+		    args->local_integ_key, args->local_integ_key_len);
+
+      rv = ipsec_sa_add (ipsec_tun_mk_output_sa_id (dev_instance),
+			 args->local_spi,
+			 IPSEC_PROTOCOL_ESP,
+			 args->crypto_alg,
+			 &crypto_key,
+			 args->integ_alg,
+			 &integ_key,
+			 flags,
+			 args->tx_table_id,
+			 &args->local_ip,
+			 &args->remote_ip, &t->output_sa_index);
+
+      if (rv)
+	return VNET_API_ERROR_UNIMPLEMENTED;
 
       hash_set (im->ipsec_if_pool_index_by_key, key,
 		t - im->tunnel_interfaces);
@@ -392,18 +395,14 @@
 
       vnet_delete_hw_interface (vnm, t->hw_if_index);
 
-      /* delete input and output SA */
-
-      sa = pool_elt_at_index (im->sad, t->input_sa_index);
-      pool_put (im->sad, sa);
-
-      sa = pool_elt_at_index (im->sad, t->output_sa_index);
-      pool_put (im->sad, sa);
-
       hash_unset (im->ipsec_if_pool_index_by_key, key);
       hash_unset (im->ipsec_if_real_dev_by_show_dev, t->show_instance);
 
       pool_put (im->tunnel_interfaces, t);
+
+      /* delete input and output SA */
+      ipsec_sa_del (ipsec_tun_mk_input_sa_id (p[0]));
+      ipsec_sa_del (ipsec_tun_mk_output_sa_id (p[0]));
     }
 
   if (sw_if_index)
@@ -493,29 +492,25 @@
     {
       sa = pool_elt_at_index (im->sad, t->output_sa_index);
       sa->crypto_alg = alg;
-      sa->crypto_key_len = vec_len (key);
-      clib_memcpy (sa->crypto_key, key, vec_len (key));
+      ipsec_mk_key (&sa->crypto_key, key, vec_len (key));
     }
   else if (type == IPSEC_IF_SET_KEY_TYPE_LOCAL_INTEG)
     {
       sa = pool_elt_at_index (im->sad, t->output_sa_index);
       sa->integ_alg = alg;
-      sa->integ_key_len = vec_len (key);
-      clib_memcpy (sa->integ_key, key, vec_len (key));
+      ipsec_mk_key (&sa->integ_key, key, vec_len (key));
     }
   else if (type == IPSEC_IF_SET_KEY_TYPE_REMOTE_CRYPTO)
     {
       sa = pool_elt_at_index (im->sad, t->input_sa_index);
       sa->crypto_alg = alg;
-      sa->crypto_key_len = vec_len (key);
-      clib_memcpy (sa->crypto_key, key, vec_len (key));
+      ipsec_mk_key (&sa->crypto_key, key, vec_len (key));
     }
   else if (type == IPSEC_IF_SET_KEY_TYPE_REMOTE_INTEG)
     {
       sa = pool_elt_at_index (im->sad, t->input_sa_index);
       sa->integ_alg = alg;
-      sa->integ_key_len = vec_len (key);
-      clib_memcpy (sa->integ_key, key, vec_len (key));
+      ipsec_mk_key (&sa->integ_key, key, vec_len (key));
     }
   else
     return VNET_API_ERROR_INVALID_VALUE;
diff --git a/src/vnet/ipsec/ipsec_if.h b/src/vnet/ipsec/ipsec_if.h
index 96a109d..67d5554 100644
--- a/src/vnet/ipsec/ipsec_if.h
+++ b/src/vnet/ipsec/ipsec_if.h
@@ -41,7 +41,7 @@
   u8 is_add;
   u8 esn;
   u8 anti_replay;
-  ip4_address_t local_ip, remote_ip;
+  ip46_address_t local_ip, remote_ip;
   u32 local_spi;
   u32 remote_spi;
   ipsec_crypto_alg_t crypto_alg;
diff --git a/src/vnet/ipsec/ipsec_if_in.c b/src/vnet/ipsec/ipsec_if_in.c
index 1dbd127..833f848 100644
--- a/src/vnet/ipsec/ipsec_if_in.c
+++ b/src/vnet/ipsec/ipsec_if_in.c
@@ -25,7 +25,8 @@
 /* Statistics (not really errors) */
 #define foreach_ipsec_if_input_error				  \
 _(RX, "good packets received")					  \
-_(DISABLED, "ipsec packets received on disabled interface")
+_(DISABLED, "ipsec packets received on disabled interface")       \
+_(NO_TUNNEL, "no matching tunnel")
 
 static char *ipsec_if_input_error_strings[] = {
 #define _(sym,string) string,
@@ -76,7 +77,7 @@
   ipsec_sa_t *sa0;
   vlib_combined_counter_main_t *rx_counter;
   vlib_combined_counter_main_t *drop_counter;
-  u32 n_disabled = 0;
+  u32 n_disabled = 0, n_no_tunnel = 0;
 
   rx_counter = vim->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX;
   drop_counter = vim->combined_sw_if_counters + VNET_INTERFACE_COUNTER_DROP;
@@ -181,6 +182,11 @@
 	      vlib_buffer_advance (b0, ip4_header_bytes (ip0));
 	      next0 = im->esp4_decrypt_next_index;
 	    }
+	  else
+	    {
+	      b0->error = node->errors[IPSEC_IF_INPUT_ERROR_NO_TUNNEL];
+	      n_no_tunnel++;
+	    }
 
 	trace:
 	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
@@ -216,6 +222,8 @@
 
   vlib_node_increment_counter (vm, ipsec_if_input_node.index,
 			       IPSEC_IF_INPUT_ERROR_DISABLED, n_disabled);
+  vlib_node_increment_counter (vm, ipsec_if_input_node.index,
+			       IPSEC_IF_INPUT_ERROR_DISABLED, n_no_tunnel);
 
   return from_frame->n_vectors;
 }
diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c
index d439b4d..f20d941 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 @@
   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 @@
   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 @@
 }
 
 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_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 @@
   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 @@
   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
  *
diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h
index 43d699b..775343b 100644
--- a/src/vnet/ipsec/ipsec_sa.h
+++ b/src/vnet/ipsec/ipsec_sa.h
@@ -17,6 +17,7 @@
 
 #include <vlib/vlib.h>
 #include <vnet/ip/ip.h>
+#include <vnet/fib/fib_node.h>
 
 #define foreach_ipsec_crypto_alg    \
   _ (0, NONE, "none")               \
@@ -63,19 +64,50 @@
   IPSEC_PROTOCOL_ESP = 1
 } ipsec_protocol_t;
 
+#define IPSEC_N_PROTOCOLS (IPSEC_PROTOCOL_ESP+1)
+
+#define IPSEC_KEY_MAX_LEN 128
+typedef struct ipsec_key_t_
+{
+  u8 len;
+  u8 data[IPSEC_KEY_MAX_LEN];
+} ipsec_key_t;
+
+/*
+ * Enable extended sequence numbers
+ * Enable Anti-replay
+ * IPsec tunnel mode if non-zero, else transport mode
+ * IPsec tunnel mode is IPv6 if non-zero,
+ * else IPv4 tunnel only valid if is_tunnel is non-zero
+ * enable UDP encapsulation for NAT traversal
+ */
+#define foreach_ipsec_sa_flags                            \
+  _ (0, NONE, "none")                                     \
+  _ (1, USE_EXTENDED_SEQ_NUM, "esn")                      \
+  _ (2, USE_ANTI_REPLAY, "anti-replay")                   \
+  _ (4, IS_TUNNEL, "tunnel")                              \
+  _ (8, IS_TUNNEL_V6, "tunnel-v6")                        \
+  _ (16, UDP_ENCAP, "udp-encap")                          \
+
+typedef enum ipsec_sad_flags_t_
+{
+#define _(v, f, s) IPSEC_SA_FLAG_##f = v,
+  foreach_ipsec_sa_flags
+#undef _
+} ipsec_sa_flags_t;
+
 typedef struct
 {
+  fib_node_t node;
   u32 id;
   u32 spi;
   ipsec_protocol_t protocol;
 
   ipsec_crypto_alg_t crypto_alg;
-  u8 crypto_key_len;
-  u8 crypto_key[128];
+  ipsec_key_t crypto_key;
 
   ipsec_integ_alg_t integ_alg;
-  u8 integ_key_len;
-  u8 integ_key[128];
+  ipsec_key_t integ_key;
 
   u8 use_esn;
   u8 use_anti_replay;
@@ -86,6 +118,10 @@
   ip46_address_t tunnel_src_addr;
   ip46_address_t tunnel_dst_addr;
 
+  fib_node_index_t fib_entry_index;
+  u32 sibling;
+  dpo_id_t dpo[IPSEC_N_PROTOCOLS];
+
   u32 tx_fib_index;
   u32 salt;
 
@@ -100,18 +136,36 @@
   u64 total_data_size;
 } ipsec_sa_t;
 
-extern int ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa,
-			     int is_add);
+extern void ipsec_mk_key (ipsec_key_t * key, const u8 * data, u8 len);
+
+extern int 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 * tunnel_src_addr,
+			 const ip46_address_t * tunnel_dst_addr,
+			 u32 * sa_index);
+extern u32 ipsec_sa_del (u32 id);
+
 extern u8 ipsec_is_sa_used (u32 sa_index);
-extern int ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update);
+extern int ipsec_set_sa_key (u32 id,
+			     const ipsec_key_t * ck, const ipsec_key_t * ik);
 extern u32 ipsec_get_sa_index_by_sa_id (u32 sa_id);
 
 extern u8 *format_ipsec_crypto_alg (u8 * s, va_list * args);
 extern u8 *format_ipsec_integ_alg (u8 * s, va_list * args);
+extern u8 *format_ipsec_sa (u8 * s, va_list * args);
+extern u8 *format_ipsec_key (u8 * s, va_list * args);
 extern uword unformat_ipsec_crypto_alg (unformat_input_t * input,
 					va_list * args);
 extern uword unformat_ipsec_integ_alg (unformat_input_t * input,
 				       va_list * args);
+extern uword unformat_ipsec_key (unformat_input_t * input, va_list * args);
 
 #endif /* __IPSEC_SPD_SA_H__ */