summaryrefslogtreecommitdiffstats
path: root/test/test_ip6.py
AgeCommit message (Expand)AuthorFilesLines
2017-09-11FIB table add/delete APINeale Ranns1-2/+5
2017-08-08L2 over MPLSNeale Ranns1-13/+13
2017-08-01Tests for recursive load-balancing with no choices.Neale Ranns1-0/+33
2017-05-26ARP/ND use path_remove to complement path_addNeale Ranns1-0/+117
2017-05-25MPLS hash function improvementsNeale Ranns1-23/+94
2017-04-26IP Flow Hash Config fixesNeale Ranns1-0/+149
2017-04-19Learn IP6 link-local ND entries from NSs sourced from link-local addressNeale Ranns1-26/+80
2017-03-17Fix IP feature ordering.Neale Ranns1-1/+99
2017-03-09IMplementation for option to not create a FIB table entry when adding a neigh...Neale Ranns1-2/+26
2017-03-09Tests to target holes in adjacency and DPO test coverageNeale Ranns1-1/+70
2017-03-08Proxy ND (RFC4389 - or a sub-set thereof). This allows the 'emulation' of bri...Neale Ranns1-17/+276
2017-03-03IPv6 RA improvementsNeale Ranns1-5/+214
2017-02-03pep8 compliance for test_ip6.pyNeale Ranns1-3/+3
2017-01-27IP Multicast FIB (mfib)Neale Ranns1-78/+53
2017-01-11make test: improve documentation and PEP8 complianceKlement Sekera1-27/+30
2017-01-10IPv6 NS RS tests and fixesNeale Ranns1-1/+236
2016-12-23make test: improve handling of packet capturesKlement Sekera1-2/+1
2016-12-05make test: fix missing log/packet messagesKlement Sekera1-7/+6
2016-11-15Update test documentation.Matej Klotton1-15/+62
2016-10-26refactor test frameworkKlement Sekera1-219/+104
2016-10-03test: new test infrastructureDamjan Marion1-0/+281
n412' href='#n412'>412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
/*
 * ah_encrypt.c : IPSec AH encrypt node
 *
 * Copyright (c) 2015 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 <vnet/api_errno.h>
#include <vnet/ip/ip.h>

#include <vnet/ipsec/ipsec.h>
#include <vnet/ipsec/esp.h>
#include <vnet/ipsec/ah.h>

#define foreach_ah_encrypt_next \
  _ (DROP, "error-drop")            \
  _ (IP4_LOOKUP, "ip4-lookup")      \
  _ (IP6_LOOKUP, "ip6-lookup")      \
  _ (INTERFACE_OUTPUT, "interface-output")


#define _(v, s) AH_ENCRYPT_NEXT_##v,
typedef enum
{
  foreach_ah_encrypt_next
#undef _
    AH_ENCRYPT_N_NEXT,
} ah_encrypt_next_t;

#define foreach_ah_encrypt_error                                \
 _(RX_PKTS, "AH pkts received")                                 \
 _(CRYPTO_ENGINE_ERROR, "crypto engine error (packet dropped)") \
 _(SEQ_CYCLED, "sequence number cycled")


typedef enum
{
#define _(sym,str) AH_ENCRYPT_ERROR_##sym,
  foreach_ah_encrypt_error
#undef _
    AH_ENCRYPT_N_ERROR,
} ah_encrypt_error_t;

static char *ah_encrypt_error_strings[] = {
#define _(sym,string) string,
  foreach_ah_encrypt_error
#undef _
};

typedef struct
{
  u32 sa_index;
  u32 spi;
  u32 seq_lo;
  u32 seq_hi;
  ipsec_integ_alg_t integ_alg;
} ah_encrypt_trace_t;

/* packet trace format function */
static u8 *
format_ah_encrypt_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  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: sa-index %d spi %u (0x%08x) seq %u:%u integrity %U",
	      t->sa_index, t->spi, t->spi, t->seq_hi, t->seq_lo,
	      format_ipsec_integ_alg, t->integ_alg);
  return s;
}

static_always_inline void
ah_process_ops (vlib_main_t * vm, vlib_node_runtime_t * node,
		vnet_crypto_op_t * ops, vlib_buffer_t * b[], u16 * nexts)
{
  u32 n_fail, n_ops = vec_len (ops);
  vnet_crypto_op_t *op = ops;

  if (n_ops == 0)
    return;

  n_fail = n_ops - vnet_crypto_process_ops (vm, op, n_ops);

  while (n_fail)
    {
      ASSERT (op - ops < n_ops);

      if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
	{
	  u32 bi = op->user_data;
	  b[bi]->error = node->errors[AH_ENCRYPT_ERROR_CRYPTO_ENGINE_ERROR];
	  nexts[bi] = AH_ENCRYPT_NEXT_DROP;
	  n_fail--;
	}
      op++;
    }
}

typedef struct
{
  union
  {
    struct
    {
      u8 hop_limit;
      u32 ip_version_traffic_class_and_flow_label;
    };

    struct
    {
      u8 ttl;
      u8 tos;
    };
  };
  i16 current_data;
  u8 skip;
  u32 sa_index;
} ah_encrypt_packet_data_t;

always_inline uword
ah_encrypt_inline (vlib_main_t * vm,
		   vlib_node_runtime_t * node, vlib_frame_t * frame,
		   int is_ip6)
{
  u32 n_left, *from, thread_index;
  int icv_size = 0;
  from = vlib_frame_vector_args (frame);
  n_left = frame->n_vectors;
  ipsec_main_t *im = &ipsec_main;
  ah_encrypt_packet_data_t pkt_data[VLIB_FRAME_SIZE], *pd = pkt_data;
  thread_index = vm->thread_index;
  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
  u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
  ipsec_per_thread_data_t *ptd = vec_elt_at_index (im->ptd, thread_index);
  ipsec_sa_t *sa0 = 0;
  ip4_and_ah_header_t *ih0, *oh0 = 0;
  ip6_and_ah_header_t *ih6_0, *oh6_0 = 0;
  u32 current_sa_index = ~0, current_sa_bytes = 0, current_sa_pkts = 0;
  const static ip4_header_t ip4_hdr_template = {
    .ip_version_and_header_length = 0x45,
    .protocol = IP_PROTOCOL_IPSEC_AH,
  };
  const static ip6_header_t ip6_hdr_template = {
    .ip_version_traffic_class_and_flow_label = 0x60,
    .protocol = IP_PROTOCOL_IPSEC_AH,
  };

  clib_memset (pkt_data, 0, VLIB_FRAME_SIZE * sizeof (pkt_data[0]));
  vlib_get_buffers (vm, from, b, n_left);
  vec_reset_length (ptd->crypto_ops);
  vec_reset_length (ptd->integ_ops);

  while (n_left > 0)
    {
      u8 ip_hdr_size;
      u8 next_hdr_type;

      if (vnet_buffer (b[0])->ipsec.sad_index != current_sa_index)
	{
	  if (current_sa_index != ~0)
	    vlib_increment_combined_counter (&ipsec_sa_counters, thread_index,
					     current_sa_index,
					     current_sa_pkts,
					     current_sa_bytes);
	  current_sa_index = vnet_buffer (b[0])->ipsec.sad_index;
	  sa0 = pool_elt_at_index (im->sad, current_sa_index);

	  current_sa_bytes = current_sa_pkts = 0;
	}

      pd->sa_index = current_sa_index;
      next[0] = AH_ENCRYPT_NEXT_DROP;

      if (PREDICT_FALSE (esp_seq_advance (sa0)))
	{
	  b[0]->error = node->errors[AH_ENCRYPT_ERROR_SEQ_CYCLED];
	  pd->skip = 1;
	  goto next;
	}

      current_sa_pkts += 1;
      current_sa_bytes += b[0]->current_length;

      ssize_t adv;
      ih0 = vlib_buffer_get_current (b[0]);
      pd->ttl = ih0->ip4.ttl;
      pd->tos = ih0->ip4.tos;

      if (PREDICT_TRUE (ipsec_sa_is_set_IS_TUNNEL (sa0)))
	{
	  if (is_ip6)
	    adv = -sizeof (ip6_and_ah_header_t);
	  else
	    adv = -sizeof (ip4_and_ah_header_t);
	}
      else
	{
	  adv = -sizeof (ah_header_t);
	}

      icv_size = sa0->integ_icv_size;
      const u8 padding_len = ah_calc_icv_padding_len (icv_size, is_ip6);
      adv -= padding_len;
      /* transport mode save the eth header before it is overwritten */
      if (PREDICT_FALSE (!ipsec_sa_is_set_IS_TUNNEL (sa0)))
	{
	  const u32 l2_len = vnet_buffer (b[0])->ip.save_rewrite_length;
	  u8 *l2_hdr_in = (u8 *) vlib_buffer_get_current (b[0]) - l2_len;

	  u8 *l2_hdr_out = l2_hdr_in + adv - icv_size;

	  clib_memcpy_le32 (l2_hdr_out, l2_hdr_in, l2_len);
	}

      vlib_buffer_advance (b[0], adv - icv_size);

      if (is_ip6)
	{
	  ih6_0 = (ip6_and_ah_header_t *) ih0;
	  ip_hdr_size = sizeof (ip6_header_t);
	  oh6_0 = vlib_buffer_get_current (b[0]);
	  pd->current_data = b[0]->current_data;

	  pd->hop_limit = ih6_0->ip6.hop_limit;
	  pd->ip_version_traffic_class_and_flow_label =
	    ih6_0->ip6.ip_version_traffic_class_and_flow_label;
	  if (PREDICT_TRUE (ipsec_sa_is_set_IS_TUNNEL (sa0)))
	    {
	      next_hdr_type = IP_PROTOCOL_IPV6;
	    }
	  else
	    {
	      next_hdr_type = ih6_0->ip6.protocol;
	      memmove (oh6_0, ih6_0, sizeof (ip6_header_t));
	    }

	  clib_memcpy_fast (&oh6_0->ip6, &ip6_hdr_template, 8);
	  oh6_0->ah.reserved = 0;
	  oh6_0->ah.nexthdr = next_hdr_type;
	  oh6_0->ah.spi = clib_net_to_host_u32 (sa0->spi);
	  oh6_0->ah.seq_no = clib_net_to_host_u32 (sa0->seq);
	  oh6_0->ip6.payload_length =
	    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b[0]) -
				  sizeof (ip6_header_t));
	  oh6_0->ah.hdrlen =
	    (sizeof (ah_header_t) + icv_size + padding_len) / 4 - 2;
	}
      else
	{
	  ip_hdr_size = sizeof (ip4_header_t);
	  oh0 = vlib_buffer_get_current (b[0]);
	  clib_memset (oh0, 0, sizeof (ip4_and_ah_header_t));
	  pd->current_data = b[0]->current_data;

	  if (PREDICT_TRUE (ipsec_sa_is_set_IS_TUNNEL (sa0)))
	    {
	      next_hdr_type = IP_PROTOCOL_IP_IN_IP;
	    }
	  else
	    {
	      next_hdr_type = ih0->ip4.protocol;
	      memmove (oh0, ih0, sizeof (ip4_header_t));
	    }

	  clib_memcpy_fast (&oh0->ip4, &ip4_hdr_template,
			    sizeof (ip4_header_t) -
			    sizeof (ip4_address_pair_t));

	  oh0->ip4.length =
	    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b[0]));
	  oh0->ah.spi = clib_net_to_host_u32 (sa0->spi);
	  oh0->ah.seq_no = clib_net_to_host_u32 (sa0->seq);
	  oh0->ah.nexthdr = next_hdr_type;
	  oh0->ah.hdrlen =
	    (sizeof (ah_header_t) + icv_size + padding_len) / 4 - 2;
	}

      if (PREDICT_TRUE (!is_ip6 && ipsec_sa_is_set_IS_TUNNEL (sa0) &&
			!ipsec_sa_is_set_IS_TUNNEL_V6 (sa0)))
	{
	  clib_memcpy_fast (&oh0->ip4.address_pair,
			    &sa0->ip4_hdr.address_pair,
			    sizeof (ip4_address_t));

	  next[0] = sa0->dpo.dpoi_next_node;
	  vnet_buffer (b[0])->ip.adj_index[VLIB_TX] = sa0->dpo.dpoi_index;
	}
      else if (is_ip6 && ipsec_sa_is_set_IS_TUNNEL (sa0) &&
	       ipsec_sa_is_set_IS_TUNNEL_V6 (sa0))
	{
	  clib_memcpy_fast (&oh6_0->ip6.src_address,
			    &sa0->ip6_hdr.src_address,
			    sizeof (ip6_address_t) * 2);
	  next[0] = sa0->dpo.dpoi_next_node;
	  vnet_buffer (b[0])->ip.adj_index[VLIB_TX] = sa0->dpo.dpoi_index;
	}

      if (PREDICT_TRUE (sa0->integ_op_id))
	{
	  vnet_crypto_op_t *op;
	  vec_add2_aligned (ptd->integ_ops, op, 1, CLIB_CACHE_LINE_BYTES);
	  vnet_crypto_op_init (op, sa0->integ_op_id);
	  op->src = vlib_buffer_get_current (b[0]);
	  op->len = b[0]->current_length;
	  op->digest = vlib_buffer_get_current (b[0]) + ip_hdr_size +
	    sizeof (ah_header_t);
	  clib_memset (op->digest, 0, icv_size);
	  op->digest_len = icv_size;
	  op->key_index = sa0->integ_key_index;
	  op->user_data = b - bufs;
	  if (ipsec_sa_is_set_USE_ESN (sa0))
	    {
	      u32 seq_hi = clib_host_to_net_u32 (sa0->seq_hi);

	      op->len += sizeof (seq_hi);
	      clib_memcpy (op->src + b[0]->current_length, &seq_hi,
			   sizeof (seq_hi));
	    }
	}

      if (!ipsec_sa_is_set_IS_TUNNEL (sa0))
	{
	  next[0] = AH_ENCRYPT_NEXT_INTERFACE_OUTPUT;
	  vlib_buffer_advance (b[0], -sizeof (ethernet_header_t));
	}

    next:
      n_left -= 1;
      next += 1;
      pd += 1;
      b += 1;
    }

  n_left = frame->n_vectors;
  next = nexts;
  pd = pkt_data;
  b = bufs;

  vlib_node_increment_counter (vm, node->node_index,
			       AH_ENCRYPT_ERROR_RX_PKTS, n_left);
  vlib_increment_combined_counter (&ipsec_sa_counters, thread_index,
				   current_sa_index, current_sa_pkts,
				   current_sa_bytes);

  ah_process_ops (vm, node, ptd->integ_ops, bufs, nexts);

  while (n_left)
    {
      if (pd->skip)
	goto trace;

      if (is_ip6)
	{
	  oh6_0 = (ip6_and_ah_header_t *) (b[0]->data + pd->current_data);
	  oh6_0->ip6.hop_limit = pd->hop_limit;
	  oh6_0->ip6.ip_version_traffic_class_and_flow_label =
	    pd->ip_version_traffic_class_and_flow_label;
	}
      else
	{
	  oh0 = (ip4_and_ah_header_t *) (b[0]->data + pd->current_data);
	  oh0->ip4.ttl = pd->ttl;
	  oh0->ip4.tos = pd->tos;
	  oh0->ip4.checksum = ip4_header_checksum (&oh0->ip4);
	}

    trace:
      if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
	{
	  sa0 = vec_elt_at_index (im->sad, pd->sa_index);
	  ah_encrypt_trace_t *tr =
	    vlib_add_trace (vm, node, b[0], sizeof (*tr));
	  tr->spi = sa0->spi;
	  tr->seq_lo = sa0->seq;
	  tr->seq_hi = sa0->seq_hi;
	  tr->integ_alg = sa0->integ_alg;
	  tr->sa_index = pd->sa_index;
	}

      n_left -= 1;
      next += 1;
      pd += 1;
      b += 1;
    }

  n_left = frame->n_vectors;
  vlib_buffer_enqueue_to_next (vm, node, from, nexts, n_left);

  return n_left;
}

VLIB_NODE_FN (ah4_encrypt_node) (vlib_main_t * vm,
				 vlib_node_runtime_t * node,
				 vlib_frame_t * from_frame)
{
  return ah_encrypt_inline (vm, node, from_frame, 0 /* is_ip6 */ );
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ah4_encrypt_node) = {
  .name = "ah4-encrypt",
  .vector_size = sizeof (u32),
  .format_trace = format_ah_encrypt_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = ARRAY_LEN(ah_encrypt_error_strings),
  .error_strings = ah_encrypt_error_strings,

  .n_next_nodes = AH_ENCRYPT_N_NEXT,
  .next_nodes = {
#define _(s,n) [AH_ENCRYPT_NEXT_##s] = n,
    foreach_ah_encrypt_next
#undef _
  },
};
/* *INDENT-ON* */

VLIB_NODE_FN (ah6_encrypt_node) (vlib_main_t * vm,
				 vlib_node_runtime_t * node,
				 vlib_frame_t * from_frame)
{
  return ah_encrypt_inline (vm, node, from_frame, 1 /* is_ip6 */ );
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ah6_encrypt_node) = {
  .name = "ah6-encrypt",
  .vector_size = sizeof (u32),
  .format_trace = format_ah_encrypt_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = ARRAY_LEN(ah_encrypt_error_strings),
  .error_strings = ah_encrypt_error_strings,

  .n_next_nodes = AH_ENCRYPT_N_NEXT,
  .next_nodes = {
#define _(s,n) [AH_ENCRYPT_NEXT_##s] = n,
    foreach_ah_encrypt_next
#undef _
  },
};
/* *INDENT-ON* */

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