+ u32 sw_if_index0, sw_if_index1;
+ ip4_header_t *ip40, *ip41;
+ ip6_header_t *ip60, *ip61;
+ esp_header_t *esp0, *esp1;
+ u32 len0, len1;
+ u16 buf_adv0, buf_adv1;
+ u16 buf_rewind0, buf_rewind1;
+ u32 tid0, tid1;
+ ipsec_tunnel_if_t *t0, *t1;
+ ipsec4_tunnel_key_t key40, key41;
+ ipsec6_tunnel_key_t key60, key61;
+
+ if (n_left_from >= 4)
+ {
+ CLIB_PREFETCH (b[2], CLIB_CACHE_LINE_BYTES, STORE);
+ CLIB_PREFETCH (b[2]->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (b[3], CLIB_CACHE_LINE_BYTES, STORE);
+ CLIB_PREFETCH (b[3]->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ ip40 =
+ (ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset);
+ ip41 =
+ (ip4_header_t *) (b[1]->data + vnet_buffer (b[1])->l3_hdr_offset);
+
+ if (is_ip6)
+ {
+ ip60 = (ip6_header_t *) ip40;
+ ip61 = (ip6_header_t *) ip41;
+ esp0 = (esp_header_t *) ((u8 *) ip60 + sizeof (ip6_header_t));
+ esp1 = (esp_header_t *) ((u8 *) ip61 + sizeof (ip6_header_t));
+ buf_adv0 = sizeof (ip6_header_t);
+ buf_adv1 = sizeof (ip6_header_t);
+ }
+ else
+ {
+ /* NAT UDP port 4500 case, don't advance any more */
+ if (ip40->protocol == IP_PROTOCOL_UDP)
+ {
+ esp0 =
+ (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40) +
+ sizeof (udp_header_t));
+ buf_adv0 = 0;
+ buf_rewind0 = ip4_header_bytes (ip40) + sizeof (udp_header_t);
+ }
+ else
+ {
+ esp0 = (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40));
+ buf_rewind0 = buf_adv0 = ip4_header_bytes (ip40);
+ }
+ /* NAT UDP port 4500 case, don't advance any more */
+ if (ip41->protocol == IP_PROTOCOL_UDP)
+ {
+ esp1 =
+ (esp_header_t *) ((u8 *) ip41 + ip4_header_bytes (ip41) +
+ sizeof (udp_header_t));
+ buf_adv1 = 0;
+ buf_rewind1 = ip4_header_bytes (ip41) + sizeof (udp_header_t);
+ }
+ else
+ {
+ esp1 = (esp_header_t *) ((u8 *) ip41 + ip4_header_bytes (ip41));
+ buf_rewind1 = buf_adv1 = ip4_header_bytes (ip41);
+ }
+ }
+
+ vlib_buffer_advance (b[0], buf_adv0);
+ vlib_buffer_advance (b[1], buf_adv1);
+
+ len0 = vlib_buffer_length_in_chain (vm, b[0]);
+ len1 = vlib_buffer_length_in_chain (vm, b[1]);
+
+ /* If the packet is too short to contain an SPI, then maybe
+ * it's a keep alive */
+ if (len0 < sizeof (esp_header_t))
+ {
+ if (esp0->spi_bytes[0] == 0xff)
+ b[0]->error = node->errors[IPSEC_IF_INPUT_ERROR_NAT_KEEPALIVE];
+ else
+ b[0]->error = node->errors[IPSEC_IF_INPUT_ERROR_TOO_SHORT];
+
+ next[0] = IPSEC_INPUT_NEXT_DROP;
+ goto pkt1;
+ }
+
+ if (is_ip6)
+ {
+ key60.remote_ip = ip60->src_address;
+ key60.spi = esp0->spi;
+
+ if (memcmp (&key60, &last_key6, sizeof (last_key6)) == 0)
+ {
+ tid0 = last_tunnel_id;
+ }
+ else
+ {
+ uword *p =
+ hash_get_mem (im->ipsec6_if_pool_index_by_key, &key60);
+ if (p)
+ {
+ tid0 = p[0];
+ last_tunnel_id = tid0;
+ clib_memcpy_fast (&last_key6, &key60, sizeof (key60));
+ }
+ else
+ {
+ next[0] =
+ ipsec_ip6_if_no_tunnel (node, b[0], esp0, buf_adv0);
+ n_no_tunnel++;
+ goto pkt1;
+ }
+ }
+ }
+ else /* !is_ip6 */
+ {
+ key40.remote_ip = ip40->src_address;
+ key40.spi = esp0->spi;
+
+ if (key40.as_u64 == last_key4.as_u64)
+ {
+ tid0 = last_tunnel_id;
+ }
+ else
+ {
+ uword *p =
+ hash_get (im->ipsec4_if_pool_index_by_key, key40.as_u64);
+ if (p)
+ {
+ tid0 = p[0];
+ last_tunnel_id = tid0;
+ last_key4.as_u64 = key40.as_u64;
+ }
+ else
+ {
+ next[0] =
+ ipsec_ip4_if_no_tunnel (node, b[0], esp0, ip40,
+ buf_rewind0);
+ n_no_tunnel++;
+ goto pkt1;
+ }
+ }
+ }