summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilip Tehlar <ftehlar@cisco.com>2021-01-13 16:28:10 +0000
committerFilip Tehlar <ftehlar@cisco.com>2021-01-13 16:31:49 +0000
commit887f1aa725ed64c3c83aca557bd7bb6588bacaf7 (patch)
tree256a40f5bc0563ac52ab20927151b958c8bc7f31
parentbf93c6e9bf340e323157ca2b4ffa8e83a36e5108 (diff)
ikev2: remove assert condition
Remove assert condition ensuring that a packet was punted with reason spi=0. We can't rely on data in punt_reason because it is defind in an union. This patch adds a new IKE node that handles punted IKE packets separately. Type: fix Change-Id: I2e1b44922e53e049bd8512fa5cb85cee6a2b8aa7 Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
-rw-r--r--src/plugins/ikev2/ikev2.c55
1 files changed, 36 insertions, 19 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c
index 8d06bd3a382..cfc3d9f9940 100644
--- a/src/plugins/ikev2/ikev2.c
+++ b/src/plugins/ikev2/ikev2.c
@@ -2834,9 +2834,8 @@ ikev2_generate_sa_init_data_and_log (ikev2_sa_t * sa)
}
static_always_inline uword
-ikev2_node_internal (vlib_main_t * vm,
- vlib_node_runtime_t * node, vlib_frame_t * frame,
- u8 is_ip4)
+ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame, u8 is_ip4, u8 natt)
{
u32 n_left = frame->n_vectors, *from;
ikev2_main_t *km = &ikev2_main;
@@ -2862,15 +2861,9 @@ ikev2_node_internal (vlib_main_t * vm,
ikev2_sa_t sa; /* temporary store for SA */
u32 rlen, slen = 0;
int ip_hdr_sz = 0;
- int is_req = 0, has_non_esp_marker = 0;
+ int is_req = 0;
- ASSERT (0 == b0->punt_reason
- || (is_ip4
- && b0->punt_reason ==
- ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0]));
-
- if (is_ip4
- && b0->punt_reason == ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0])
+ if (natt)
{
u8 *ptr = vlib_buffer_get_current (b0);
ip40 = (ip4_header_t *) ptr;
@@ -2903,12 +2896,12 @@ ikev2_node_internal (vlib_main_t * vm,
rlen = b0->current_length - ip_hdr_sz - sizeof (*udp0);
/* check for non-esp marker */
- if (*((u32 *) ike0) == 0)
+ if (natt)
{
+ ASSERT (*((u32 *) ike0) == 0);
ike0 =
(ike_header_t *) ((u8 *) ike0 + sizeof (ikev2_non_esp_marker));
rlen -= sizeof (ikev2_non_esp_marker);
- has_non_esp_marker = 1;
}
if (clib_net_to_host_u32 (ike0->length) != rlen)
@@ -3273,13 +3266,13 @@ ikev2_node_internal (vlib_main_t * vm,
if (udp0->dst_port == clib_net_to_host_u16 (IKEV2_PORT_NATT)
&& ikev2_natt_active (sa0))
{
- if (!has_non_esp_marker)
+ if (!natt)
slen = ikev2_insert_non_esp_marker (ike0, slen);
}
}
else
{
- if (has_non_esp_marker)
+ if (natt)
slen += sizeof (ikev2_non_esp_marker);
u16 tp = udp0->dst_port;
@@ -3334,13 +3327,20 @@ ikev2_node_internal (vlib_main_t * vm,
static uword
ikev2_ip4 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
{
- return ikev2_node_internal (vm, node, frame, 1 /* is_ip4 */ );
+ return ikev2_node_internal (vm, node, frame, 1 /* is_ip4 */, 0);
+}
+
+static uword
+ikev2_ip4_natt (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame)
+{
+ return ikev2_node_internal (vm, node, frame, 1 /* is_ip4 */, 1 /* natt */);
}
static uword
ikev2_ip6 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
{
- return ikev2_node_internal (vm, node, frame, 0 /* is_ip4 */ );
+ return ikev2_node_internal (vm, node, frame, 0 /* is_ip4 */, 0);
}
/* *INDENT-OFF* */
@@ -3361,6 +3361,23 @@ VLIB_REGISTER_NODE (ikev2_node_ip4,static) = {
},
};
+VLIB_REGISTER_NODE (ikev2_node_ip4_natt,static) = {
+ .function = ikev2_ip4_natt,
+ .name = "ikev2-ip4-natt",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ikev2_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(ikev2_error_strings),
+ .error_strings = ikev2_error_strings,
+
+ .n_next_nodes = IKEV2_IP4_N_NEXT,
+ .next_nodes = {
+ [IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop",
+ },
+};
+
VLIB_REGISTER_NODE (ikev2_node_ip6,static) = {
.function = ikev2_ip6,
.name = "ikev2-ip6",
@@ -4666,9 +4683,9 @@ ikev2_init (vlib_main_t * vm)
udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip4.index, 1);
udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip6.index, 0);
- vlib_punt_hdl_t punt_hdl = vlib_punt_client_register ("ikev2-ip4");
+ vlib_punt_hdl_t punt_hdl = vlib_punt_client_register ("ikev2-ip4-natt");
vlib_punt_register (punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0],
- "ikev2-ip4");
+ "ikev2-ip4-natt");
ikev2_cli_reference ();
km->log_level = IKEV2_LOG_ERROR;
ref='#n364'>364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 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 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
/*
 * Copyright (c) 2017-2019 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.
 */

#ifndef __HICN_PCS_H__
#define __HICN_PCS_H__

#include "hashtb.h"
#include "face_db.h"
#include "strategy_dpo_manager.h"
#include "error.h"
#include "cache_policies/cs_policy.h"
#include "faces/face.h"
#include "faces/ip/dpo_ip.h"
#include "faces/app/face_prod.h"

/* The PIT and CS are stored as a union */
#define HICN_PIT_NULL_TYPE 0
#define HICN_PIT_TYPE      1
#define HICN_CS_TYPE       2

/*
 * Definitions and Forward refs for the time counters we're trying out.
 * Counters are maintained by the background process.
 */
#define SEC_MS 1000
#define HICN_INFRA_FAST_TIMER_SECS  1
#define HICN_INFRA_FAST_TIMER_MSECS (HICN_INFRA_FAST_TIMER_SECS * SEC_MS)
#define HICN_INFRA_SLOW_TIMER_SECS  60
#define HICN_INFRA_SLOW_TIMER_MSECS (HICN_INFRA_SLOW_TIMER_SECS * SEC_MS)

/*
 * Max number of incoming (interest) faces supported, for now. Note that
 * changing this may change alignment within the PIT struct, so be careful.
 */
typedef struct __attribute__ ((packed)) hicn_pcs_shared_s
{

  /* Installation/creation time (vpp float units, for now) */
  f64 create_time;

  /* Expiration time (vpp float units, for now) */
  f64 expire_time;

  /* Shared 'flags' octet */
  u8 entry_flags;

  /* Needed to align for the pit or cs portion */
  u8 padding;
} hicn_pcs_shared_t;

#define HICN_PCS_ENTRY_CS_FLAG 0x01

/*
 * PIT entry, unioned with a CS entry below
 */
typedef struct __attribute__ ((packed)) hicn_pit_entry_s
{

  /* Shared size 8 + 8 + 2 = 18B */

  /*
   * Egress next hop (containes the egress face) This id refers to the
   * nh
   */
  /* choosen in the next_hops array of the dpo */
  /* 18B + 1B = 19B */
  u8 pe_txnh;

  /* Array of faces */
  /* 24B + 32B (8B*4) =56B */
  hicn_face_db_t faces;

} hicn_pit_entry_t;

#define HICN_CS_ENTRY_OPAQUE_SIZE HICN_HASH_NODE_APP_DATA_SIZE - 40

/*
 * CS entry, unioned with a PIT entry below
 */
typedef struct __attribute__ ((packed)) hicn_cs_entry_s
{
  /* 22B + 2B = 24B */
  u16 align;

  /* Packet buffer, if held */
  /* 18B + 4B = 22B */
  u32 cs_pkt_buf;

  /* Ingress face */
  /* 24B + 8B = 32B */
  //Fix alignment issues
  union
  {
    dpo_id_t cs_rxface;
    u64 cs_rxface_u64;
  };

  /* Linkage for LRU, in the form of hashtable node indexes */
  /* 32B + 8B = 40B */
  u32 cs_lru_prev;
  u32 cs_lru_next;

  /* Reserved for implementing cache policy different than LRU */
  /* 40B + 56B = 96B */
  u8 opaque[HICN_CS_ENTRY_OPAQUE_SIZE];


} __attribute__ ((packed)) hicn_cs_entry_t;

/*
 * Combined PIT/CS entry data structure, embedded in a hashtable entry after
 * the common hashtable preamble struct. This MUST fit in the available
 * (fixed) space in a hashtable node.
 */
typedef struct hicn_pcs_entry_s
{

  hicn_pcs_shared_t shared;

  union
  {
    hicn_pit_entry_t pit;
    hicn_cs_entry_t cs;
  } u;
} hicn_pcs_entry_t;


/*
 * Overall PIT/CS table, based on the common hashtable
 */
typedef struct hicn_pit_cs_s
{

  hicn_hashtb_t *pcs_table;

  /* Counters for PIT/CS sentries */
  u32 pcs_pit_count;
  u32 pcs_cs_count;
  u32 pcs_cs_dealloc;
  u32 pcs_pit_dealloc;

  /* Total size of PCS */
  u32 pcs_size;

  /* Memory reserved for appfaces */
  u32 pcs_app_max;
  u32 pcs_app_count;

  hicn_cs_policy_t policy_state;
  hicn_cs_policy_vft_t policy_vft;

} hicn_pit_cs_t;

/* Functions declarations */
int hicn_pit_create (hicn_pit_cs_t * p, u32 num_elems);

always_inline void
hicn_pit_to_cs (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		hicn_pcs_entry_t * pcs_entry, hicn_hash_entry_t * hash_entry,
		hicn_hash_node_t * node, const hicn_dpo_vft_t * dpo_vft,
		dpo_id_t * hicn_dpo_id, dpo_id_t * inface_id, u8 is_appface);

always_inline void
hicn_pcs_cs_update (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		    hicn_pcs_entry_t * entry, hicn_hash_node_t * node);

always_inline void
hicn_pcs_cs_delete (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		    hicn_pcs_entry_t ** pcs_entry, hicn_hash_node_t ** node,
		    hicn_hash_entry_t * hash_entry,
		    const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id);

always_inline int
hicn_pcs_cs_insert (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		    hicn_pcs_entry_t * entry, hicn_hash_node_t * node,
		    hicn_hash_entry_t ** hash_entry, u64 hashval,
		    u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs,
		    u8 * hash_entry_id, u32 * bucket_id,
		    u8 * bucket_is_overflow);

always_inline int
hicn_pcs_cs_insert_update (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
			   hicn_pcs_entry_t * entry, hicn_hash_node_t * node,
			   hicn_hash_entry_t ** hash_entry, u64 hashval,
			   u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id,
			   u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id,
			   u8 * bucket_is_overflow);

always_inline int
hicn_pcs_pit_insert (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t * entry,
		     hicn_hash_node_t * node, hicn_hash_entry_t ** hash_entry,
		     u64 hashval, u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id,
		     u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id,
		     u8 * bucket_is_overflow);

always_inline void
hicn_pcs_pit_delete (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp,
		     hicn_hash_node_t ** node, vlib_main_t * vm,
		     hicn_hash_entry_t * hash_entry,
		     const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id);

always_inline int
hicn_pcs_insert (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		 hicn_pcs_entry_t * entry, hicn_hash_node_t * node,
		 hicn_hash_entry_t ** hash_entry, u64 hashval, u32 * node_id,
		 u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, u8 * hash_entry_id,
		 u32 * bucket_id, u8 * bucket_is_overflow);

always_inline void
hicn_pcs_delete (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp,
		 hicn_hash_node_t ** node, vlib_main_t * vm,
		 hicn_hash_entry_t * hash_entry,
		 const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id);

always_inline void
hicn_pcs_remove_lock (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp,
		      hicn_hash_node_t ** node, vlib_main_t * vm,
		      hicn_hash_entry_t * hash_entry,
		      const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id);

always_inline void
hicn_cs_delete_trimmed (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp,
			hicn_hash_entry_t * hash_entry,
			hicn_hash_node_t ** node, vlib_main_t * vm);

/* Function implementation */
/* Accessor for pit/cs data inside hash table node */
static inline hicn_pcs_entry_t *
hicn_pit_get_data (hicn_hash_node_t * node)
{
  return (hicn_pcs_entry_t *) (hicn_hashtb_node_data (node));
}

/* Init pit/cs data block (usually inside hash table node) */
static inline void
hicn_pit_init_data (hicn_pcs_entry_t * p)
{
  memset (p, 0, sizeof (hicn_pcs_entry_t));
  hicn_face_bucket_t *face_bkt;
  pool_get (hicn_face_bucket_pool, face_bkt);

  p->u.pit.faces.next_bucket = face_bkt - hicn_face_bucket_pool;
}



static inline f64
hicn_pcs_get_exp_time (f64 cur_time_sec, u64 lifetime_msec)
{
  return (cur_time_sec + ((f64) lifetime_msec) / SEC_MS);
}

/*
 * Configure CS LRU limit. Zero is accepted, means 'no limit', probably not a
 * good choice.
 */
static inline void
hicn_pit_set_lru_max (hicn_pit_cs_t * p, u32 limit)
{
  p->policy_state.max = limit;
}

/*
 * Configure CS LRU limit. Zero is accepted, means 'no limit', probably not a
 * good choice.
 */
static inline void
hicn_pit_set_lru_app_max (hicn_pit_cs_t * p, u32 limit)
{
  p->pcs_app_max = limit;
}

/*
 * Accessor for PIT interest counter.
 */
static inline u32
hicn_pit_get_int_count (const hicn_pit_cs_t * pitcs)
{
  return (pitcs->pcs_pit_count);
}

/*
 * Accessor for PIT cs entries counter.
 */
static inline u32
hicn_pit_get_cs_count (const hicn_pit_cs_t * pitcs)
{
  return (pitcs->pcs_cs_count);
}

static inline u32
hicn_pcs_get_ntw_count (const hicn_pit_cs_t * pitcs)
{
  return (pitcs->policy_state.count);
}

static inline u32
hicn_pit_get_htb_bucket_count (const hicn_pit_cs_t * pitcs)
{
  return (pitcs->pcs_table->ht_overflow_buckets_used);
}

static inline int
hicn_cs_enabled (hicn_pit_cs_t * pit)
{
  switch (HICN_FEATURE_CS)
    {
    case 0:
    default:
      return (0);
    case 1:
      return (pit->policy_state.max > 0);
    }
}

/*
 * Delete a PIT/CS entry from the hashtable, freeing the hash node struct.
 * The caller's pointers are zeroed! If cs_trim is true, entry has already
 * been removed from lru list The main purpose of this wrapper is helping
 * maintain the per-PIT stats.
 */
always_inline void
hicn_pcs_delete_internal (hicn_pit_cs_t * pitcs,
			  hicn_pcs_entry_t ** pcs_entryp,
			  hicn_hash_entry_t * hash_entry,
			  hicn_hash_node_t ** node, vlib_main_t * vm,
			  const hicn_dpo_vft_t * dpo_vft,
			  dpo_id_t * hicn_dpo_id)
{
  hicn_pcs_entry_t *pcs = *pcs_entryp;

  ASSERT (pcs == hicn_hashtb_node_data (*node));

  if (hash_entry->he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY)
    {
      pitcs->pcs_cs_dealloc++;

      /* Free any associated packet buffer */
      vlib_buffer_free_one (vm, pcs->u.cs.cs_pkt_buf);
      pcs->u.cs.cs_pkt_buf = ~0;
      ASSERT ((pcs->u.cs.cs_lru_prev == 0)
	      && (pcs->u.cs.cs_lru_prev == pcs->u.cs.cs_lru_next));
    }
  else
    {
      pitcs->pcs_pit_dealloc++;
      dpo_vft->hicn_dpo_unlock_dpo_ctx (hicn_dpo_id);

      /* Flush faces */
      hicn_faces_flush (&(pcs->u.pit.faces));
    }

  hicn_hashtb_delete (pitcs->pcs_table, node, hash_entry->he_msb64);
  memset (*pcs_entryp, 0, sizeof (hicn_pcs_entry_t));
  *pcs_entryp = NULL;
}

/*
 * Convert a PIT entry into a CS entry (assumes that the entry is already in
 * the hashtable.) This is primarily here to maintain the internal counters.
 */
always_inline void
hicn_pit_to_cs (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		hicn_pcs_entry_t * pcs_entry, hicn_hash_entry_t * hash_entry,
		hicn_hash_node_t * node, const hicn_dpo_vft_t * dpo_vft,
		dpo_id_t * hicn_dpo_id, dpo_id_t * inface_id, u8 is_appface)
{

  /*
   * Different from the insert node. In here we don't need to add a new
   * hash entry.
   */
  pitcs->pcs_pit_count--;
  dpo_vft->hicn_dpo_unlock_dpo_ctx (hicn_dpo_id);
  /* Flush faces */
  hicn_faces_flush (&(pcs_entry->u.pit.faces));
  memset (&(pcs_entry->u.cs), ~0, sizeof (hicn_cs_entry_t));

  hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_CS_ENTRY;
  node->hn_flags |= HICN_HASH_NODE_CS_FLAGS;
  pcs_entry->shared.entry_flags |= HICN_PCS_ENTRY_CS_FLAG;

  pcs_entry->u.cs.cs_rxface = *inface_id;

  /* Update the CS according to the policy */
  hicn_cs_policy_t *policy_state;
  hicn_cs_policy_vft_t *policy_vft;

  if (is_appface)
    {
      dpo_id_t *face_dpo = (dpo_id_t *) & (pcs_entry->u.cs.cs_rxface);
      hicn_face_t *face = hicn_dpoi_get_from_idx (face_dpo->dpoi_index);
      hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data;
      policy_state = &prod_face->policy;
      policy_vft = &prod_face->policy_vft;
    }
  else
    {
      policy_state = &pitcs->policy_state;
      policy_vft = &pitcs->policy_vft;
    }

  policy_vft->hicn_cs_insert (pitcs, node, pcs_entry, policy_state);
  pitcs->pcs_cs_count++;

  if (policy_state->count > policy_state->max)
    {
      hicn_hash_node_t *node;
      hicn_pcs_entry_t *pcs_entry;
      hicn_hash_entry_t *hash_entry;
      policy_vft->hicn_cs_delete_get (pitcs, policy_state,
				      &node, &pcs_entry, &hash_entry);


      /*
       * We don't have to decrease the lock (therefore we cannot
       * use hicn_pcs_cs_delete function)
       */
      policy_vft->hicn_cs_dequeue (pitcs, node, pcs_entry, policy_state);

      hicn_cs_delete_trimmed (pitcs, &pcs_entry, hash_entry, &node, vm);

      /* Update the global CS counter */
      pitcs->pcs_cs_count--;
    }
}

/* Functions specific for PIT or CS */

always_inline void
hicn_pcs_cs_update (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		    hicn_pcs_entry_t * entry, hicn_hash_node_t * node)
{
  hicn_cs_policy_t *policy_state;
  hicn_cs_policy_vft_t *policy_vft;

  dpo_id_t *face_dpo = (dpo_id_t *) & (entry->u.cs.cs_rxface);
  policy_state = &pitcs->policy_state;
  policy_vft = &pitcs->policy_vft;

  if (face_dpo->dpoi_type == hicn_face_ip_type)
    {
      hicn_face_t *face = hicn_dpoi_get_from_idx (face_dpo->dpoi_index);
      if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD)
	{
	  hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data;
	  policy_state = &prod_face->policy;
	  policy_vft = &prod_face->policy_vft;
	}
    }
  /* Update the CS LRU, moving this item to the head */
  policy_vft->hicn_cs_update (pitcs, node, entry, policy_state);
}

always_inline void
hicn_pcs_cs_delete (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		    hicn_pcs_entry_t ** pcs_entryp, hicn_hash_node_t ** nodep,
		    hicn_hash_entry_t * hash_entry,
		    const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id)
{
  if (!(hash_entry->he_flags & HICN_HASH_ENTRY_FLAG_DELETED))
    {
      hicn_cs_policy_t *policy_state;
      hicn_cs_policy_vft_t *policy_vft;

      dpo_id_t *face_dpo = (dpo_id_t *) & ((*pcs_entryp)->u.cs.cs_rxface);
      policy_state = &pitcs->policy_state;
      policy_vft = &pitcs->policy_vft;

      if (face_dpo->dpoi_type == hicn_face_ip_type)
	{
	  hicn_face_t *face = hicn_dpoi_get_from_idx (face_dpo->dpoi_index);
	  if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD)
	    {
	      hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data;
	      policy_state = &prod_face->policy;
	      policy_vft = &prod_face->policy_vft;
	    }
	}
      policy_vft->hicn_cs_dequeue (pitcs, (*nodep), (*pcs_entryp),
				   policy_state);

      /* Update the global CS counter */
      pitcs->pcs_cs_count--;
    }
  hash_entry->locks--;
  if (hash_entry->locks == 0)
    {
      hicn_pcs_delete_internal
	(pitcs, pcs_entryp, hash_entry, nodep, vm, dpo_vft, hicn_dpo_id);
    }
  else
    {
      hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_DELETED;
    }
}

always_inline int
hicn_pcs_cs_insert (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		    hicn_pcs_entry_t * entry, hicn_hash_node_t * node,
		    hicn_hash_entry_t ** hash_entry, u64 hashval,
		    u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs,
		    u8 * hash_entry_id, u32 * bucket_id,
		    u8 * bucket_is_overflow)
{
  ASSERT (entry == hicn_hashtb_node_data (node));

  int ret =
    hicn_hashtb_insert (pitcs->pcs_table, node, hash_entry, hashval, node_id,
			dpo_ctx_id, vft_id, is_cs, hash_entry_id, bucket_id,
			bucket_is_overflow);

  if (PREDICT_TRUE (ret == HICN_ERROR_NONE))
    {
      hicn_cs_policy_t *policy_state;
      hicn_cs_policy_vft_t *policy_vft;

      dpo_id_t *face_dpo = (dpo_id_t *) & (entry->u.cs.cs_rxface);
      policy_state = &pitcs->policy_state;
      policy_vft = &pitcs->policy_vft;

      if (face_dpo->dpoi_type == hicn_face_ip_type)
	{
	  hicn_face_t *face = hicn_dpoi_get_from_idx (face_dpo->dpoi_index);
	  if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD)
	    {
	      hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data;
	      policy_state = &prod_face->policy;
	      policy_vft = &prod_face->policy_vft;
	    }
	}
      policy_vft->hicn_cs_insert (pitcs, node, entry, policy_state);
      pitcs->pcs_cs_count++;

      if (policy_state->count > policy_state->max)
	{
	  hicn_hash_node_t *node;
	  hicn_pcs_entry_t *pcs_entry;
	  hicn_hash_entry_t *hash_entry;
	  policy_vft->hicn_cs_delete_get (pitcs, policy_state,
					  &node, &pcs_entry, &hash_entry);

	  hicn_pcs_cs_delete (vm, pitcs, &pcs_entry, &node, hash_entry, NULL,
			      NULL);
	}
    }
  return ret;
}

/*
 * Insert CS entry into the hashtable The main purpose of this wrapper is
 * helping maintain the per-PIT stats.
 */
always_inline int
hicn_pcs_cs_insert_update (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
			   hicn_pcs_entry_t * entry, hicn_hash_node_t * node,
			   hicn_hash_entry_t ** hash_entry, u64 hashval,
			   u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id,
			   u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id,
			   u8 * bucket_is_overflow)
{
  int ret;

  ASSERT (entry == hicn_hashtb_node_data (node));

  ret =
    hicn_pcs_cs_insert (vm, pitcs, entry, node, hash_entry, hashval, node_id,
			dpo_ctx_id, vft_id, is_cs, hash_entry_id, bucket_id,
			bucket_is_overflow);

  /* A content already exists in CS with the same name */
  if (ret == HICN_ERROR_HASHTB_EXIST)
    {
      /* Update the entry */
      hicn_hash_node_t *existing_node =
	hicn_hashtb_node_from_idx (pitcs->pcs_table, *node_id);
      hicn_pcs_entry_t *pitp = hicn_pit_get_data (existing_node);

      /* Free associated packet buffer and update counter */
      pitcs->pcs_cs_dealloc++;
      vlib_buffer_free_one (vm, pitp->u.cs.cs_pkt_buf);

      pitp->shared.create_time = entry->shared.create_time;
      pitp->shared.expire_time = entry->shared.expire_time;
      pitp->u.cs.cs_pkt_buf = entry->u.cs.cs_pkt_buf;
      hicn_pcs_cs_update (vm, pitcs, pitp, existing_node);
    }
  return (ret);
}

/*
 * Insert PIT entry into the hashtable The main purpose of this wrapper is
 * helping maintain the per-PIT stats.
 */
always_inline int
hicn_pcs_pit_insert (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t * entry,
		     hicn_hash_node_t * node, hicn_hash_entry_t ** hash_entry,
		     u64 hashval, u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id,
		     u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id,
		     u8 * bucket_is_overflow)
{
  ASSERT (entry == hicn_hashtb_node_data (node));

  int ret =
    hicn_hashtb_insert (pitcs->pcs_table, node, hash_entry, hashval, node_id,
			dpo_ctx_id, vft_id, is_cs, hash_entry_id, bucket_id,
			bucket_is_overflow);

  if (PREDICT_TRUE (ret == HICN_ERROR_NONE))
    pitcs->pcs_pit_count++;

  return ret;
}

always_inline void
hicn_pcs_pit_delete (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp,
		     hicn_hash_node_t ** node, vlib_main_t * vm,
		     hicn_hash_entry_t * hash_entry,
		     const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id)
{
  hash_entry->locks--;
  pitcs->pcs_pit_count--;
  if (hash_entry->locks == 0)
    {
      hicn_pcs_delete_internal
	(pitcs, pcs_entryp, hash_entry, node, vm, dpo_vft, hicn_dpo_id);
    }
  else
    {
      hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_DELETED;
    }
}


/* Generic functions for PIT/CS */

/*
 * Insert PIT/CS entry into the hashtable The main purpose of this wrapper is
 * helping maintain the per-PIT stats.
 */
always_inline int
hicn_pcs_insert (vlib_main_t * vm, hicn_pit_cs_t * pitcs,
		 hicn_pcs_entry_t * entry, hicn_hash_node_t * node,
		 hicn_hash_entry_t ** hash_entry, u64 hashval, u32 * node_id,
		 u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, u8 * hash_entry_id,
		 u32 * bucket_id, u8 * bucket_is_overflow)
{
  int ret;

  if ((*hash_entry)->he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY)
    {
      ret =
	hicn_pcs_cs_insert (vm, pitcs, entry, node, hash_entry, hashval,
			    node_id, dpo_ctx_id, vft_id, is_cs, hash_entry_id,
			    bucket_id, bucket_is_overflow);
    }
  else
    {
      ret =
	hicn_pcs_pit_insert (pitcs, entry, node, hash_entry, hashval, node_id,
			     dpo_ctx_id, vft_id, is_cs, hash_entry_id,
			     bucket_id, bucket_is_overflow);
    }

  return (ret);
}


/*
 * Delete entry if there are no pending lock on the entry, otherwise mark it
 * as to delete.
 */
always_inline void
hicn_pcs_delete (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp,
		 hicn_hash_node_t ** nodep, vlib_main_t * vm,
		 hicn_hash_entry_t * hash_entry,
		 const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id)
{
  /*
   * If the entry has already been marked as deleted, it has already
   * been dequeue
   */
  if (hash_entry->he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY)
    {
      hicn_pcs_cs_delete (vm, pitcs, pcs_entryp, nodep, hash_entry,
			  dpo_vft, hicn_dpo_id);
    }
  else
    {
      hicn_pcs_pit_delete (pitcs, pcs_entryp, nodep, vm,
			   hash_entry, dpo_vft, hicn_dpo_id);
    }
}

/*
 * Remove a lock in the entry and delete it if there are no pending lock and
 * the entry is marked as to be deleted
 */
always_inline void
hicn_pcs_remove_lock (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp,
		      hicn_hash_node_t ** node, vlib_main_t * vm,
		      hicn_hash_entry_t * hash_entry,
		      const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id)
{
  hash_entry->locks--;
  if (hash_entry->locks == 0
      && (hash_entry->he_flags & HICN_HASH_ENTRY_FLAG_DELETED))
    {
      hicn_pcs_delete_internal
	(pitcs, pcs_entryp, hash_entry, node, vm, dpo_vft, hicn_dpo_id);
    }
}

/*
 * Delete entry which has already been bulk-removed from lru list
 */
always_inline void
hicn_cs_delete_trimmed (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp,
			hicn_hash_entry_t * hash_entry,
			hicn_hash_node_t ** node, vlib_main_t * vm)
{


  if (hash_entry->locks == 0)
    {
      const hicn_dpo_vft_t *dpo_vft = hicn_dpo_get_vft (hash_entry->vft_id);
      dpo_id_t hicn_dpo_id =
	{ dpo_vft->hicn_dpo_get_type (), 0, 0, hash_entry->dpo_ctx_id };

      hicn_pcs_delete_internal
	(pitcs, pcs_entryp, hash_entry, node, vm, dpo_vft, &hicn_dpo_id);
    }
  else
    {
      hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_DELETED;
    }
}

/*
 * wrappable counter math (assumed uint16_t): return sum of addends
 */
always_inline u16
hicn_infra_seq16_sum (u16 addend1, u16 addend2)
{
  return (addend1 + addend2);
}

/*
 * for comparing wrapping numbers, return lt,eq,gt 0 for a lt,eq,gt b
 */
always_inline int
hicn_infra_seq16_cmp (u16 a, u16 b)
{
  return ((int16_t) (a - b));
}

/*
 * below are wrappers for lt, le, gt, ge seq16 comparators
 */
always_inline int
hicn_infra_seq16_lt (u16 a, u16 b)
{
  return (hicn_infra_seq16_cmp (a, b) < 0);
}

always_inline int
hicn_infra_seq16_le (u16 a, u16 b)
{
  return (hicn_infra_seq16_cmp (a, b) <= 0);
}

always_inline int
hicn_infra_seq16_gt (u16 a, u16 b)
{
  return (hicn_infra_seq16_cmp (a, b) > 0);
}

always_inline int
hicn_infra_seq16_ge (u16 a, u16 b)
{
  return (hicn_infra_seq16_cmp (a, b) >= 0);
}


extern u16 hicn_infra_fast_timer;	/* Counts at 1 second intervals */
extern u16 hicn_infra_slow_timer;	/* Counts at 1 minute intervals */

/*
 * Utilities to convert lifetime into expiry time based on compressed clock,
 * suitable for the opportunistic hashtable entry timeout processing.
 */

//convert time in msec to time in clicks
always_inline u16
hicn_infra_ms2clicks (u64 time_ms, u64 ms_per_click)
{
  f64 time_clicks =
    ((f64) (time_ms + ms_per_click - 1)) / ((f64) ms_per_click);
  return ((u16) time_clicks);
}

always_inline u16
hicn_infra_get_fast_exp_time (u64 lifetime_ms)
{
  u16 lifetime_clicks =
    hicn_infra_ms2clicks (lifetime_ms, HICN_INFRA_FAST_TIMER_MSECS);
  return (hicn_infra_seq16_sum (hicn_infra_fast_timer, lifetime_clicks));
}

always_inline u16
hicn_infra_get_slow_exp_time (u64 lifetime_ms)
{
  u16 lifetime_clicks =
    hicn_infra_ms2clicks (lifetime_ms, HICN_INFRA_SLOW_TIMER_MSECS);
  return (hicn_infra_seq16_sum (hicn_infra_slow_timer, lifetime_clicks));
}

#endif /* // __HICN_PCS_H__ */

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