/*
 * 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/plugin/plugin.h>
#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/pg/pg.h>
#include <vppinfra/error.h>
#include <vppinfra/pool.h>

#include <vnet/ip/ip.h>
#include <vnet/ethernet/ethernet.h>

#include "cnat_db.h"
#include "cnat_global.h"
#include "cnat_cli.h"
#include "cnat_config.h"
#include "cnat_logging.h"
#include "cnat_config_api.h"
#include "cnat_show_api.h"
#include "cnat_show_response.h"
#include "cnat_ipv4_udp.h"
#include "cnat_common_api.h"

#include <arpa/inet.h>

typedef struct {
  u32 cached_next_index;

  /* inside, outside interface handles */
  u32 * inside_sw_if_index_table;
  u32 * outside_sw_if_index_table;

  /* convenience variables */
  vlib_main_t * vlib_main;
  vnet_main_t * vnet_main;
  u8 cnat_db_initalized;
} vcgn_classify_main_t;

typedef struct {
  /* $$$$ fill in with per-pkt trace data */ 
  u32 next_index;
  u32 sw_if_index;
  u32 orig_dst_address;
  u16 orig_dst_port;
} vcgn_classify_trace_t;

#define FIND_MY_VRF_USING_I_VRF_ID                                       \
    my_vrfmap_found = 0;                                                 \
    pool_foreach (my_vrfmap, cnat_map_by_vrf, ({                         \
        if (my_vrfmap->i_vrf_id == i_vrf_id) {                           \
            my_vrfmap_found = 1;                                         \
            my_vrfmap_temp = my_vrfmap;                                  \
            break;                                                       \
        }                                                                \
    }));


/* packet trace format function */
static u8 * format_swap_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 *);
  vcgn_classify_trace_t * t = va_arg (*args, vcgn_classify_trace_t *);
  
  s = format (s, "VCGN_CLASSIFY: dst %U dst_port %d sw_if_index %d next %d",
              format_ip4_address, (ip4_header_t *) &t->orig_dst_address,
              clib_net_to_host_u16(t->orig_dst_port), 
              t->sw_if_index, t->next_index);
  return s;
}

vcgn_classify_main_t vcgn_classify_main;

vlib_node_registration_t vcgn_classify_node;

#define foreach_vcgn_classify_error \
_(PACKETS_RECEIVED,     "total packets received")   \
_(V4_PACKETS_PROCESSED, "ipv4 packets processed for vCGN")   \
_(V4_PACKETS_PUNTED,    "ipv4 packets punted")   \
_(V6_PACKETS_PUNTED,    "ipv6 packets punted")   \
_(MPLS_PACKETS_PUNTED,  "mpls unicast packets punted")   \
_(ETH_PACKETS_PUNTED,   "ethernet packets punted")


typedef enum {
#define _(sym,str) VCGN_CLASSIFY_ERROR_##sym,
  foreach_vcgn_classify_error
#undef _
  VCGN_CLASSIFY_N_ERROR,
} vcgn_classify_error_t;

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

/* 
 * To drop a pkt and increment one of the previous counters:
 * 
 * set b0->error = error_node->errors[VCGN_CLASSIFY_ERROR_EXAMPLE];
 * set next0 to a disposition index bound to "error-drop".
 *
 * To manually increment the specific counter VCGN_CLASSIFY_ERROR_EXAMPLE:
 *
 *  vlib_node_t *n = vlib_get_node (vm, vcgn_classify.index);
 *  u32 node_counter_base_index = n->error_heap_index;
 *  vlib_error_main_t * em = &vm->error_main;
 *  em->counters[node_counter_base_index + VCGN_CLASSIFY_ERROR_EXAMPLE] += 1;
 * 
 */

typedef enum {
  VCGN_CLASSIFY_NEXT_IP4_INPUT,
  VCGN_CLASSIFY_NEXT_IP6_INPUT,
  VCGN_CLASSIFY_NEXT_MPLS_INPUT,
  VCGN_CLASSIFY_NEXT_ETHERNET_INPUT,
  VCGN_CLASSIFY_NEXT_UDP_INSIDE,
  VCGN_CLASSIFY_NEXT_UDP_OUTSIDE,
  VCGN_CLASSIFY_NEXT_TCP_INSIDE,
  VCGN_CLASSIFY_NEXT_TCP_OUTSIDE,
  VCGN_CLASSIFY_NEXT_ICMP_Q_INSIDE,
  VCGN_CLASSIFY_NEXT_ICMP_Q_OUTSIDE,
  VCGN_CLASSIFY_NEXT_ICMP_E_INSIDE,
  VCGN_CLASSIFY_NEXT_ICMP_E_OUTSIDE,
  VCGN_CLASSIFY_N_NEXT,
} vcgn_classify_next_t;

static uword
vcgn_classify_node_fn (vlib_main_t * vm,
		  vlib_node_runtime_t * node,
		  vlib_frame_t * frame)
{
  u32 n_left_from, * from, * to_next;
  vcgn_classify_next_t next_index;
  vcgn_classify_main_t * vcm = &vcgn_classify_main;
  vlib_node_t *n = vlib_get_node (vm, vcgn_classify_node.index);
  u32 node_counter_base_index = n->error_heap_index;
  vlib_error_main_t * em = &vm->error_main;
  u16 *l3_type;
  int counter;

  from = vlib_frame_vector_args (frame);
  n_left_from = frame->n_vectors;
  next_index = node->cached_next_index;

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

      vlib_get_next_frame (vm, node, next_index,
			   to_next, n_left_to_next);

      #if 0
      while (n_left_from >= 4 && n_left_to_next >= 2)
      {
          u32 bi0, bi1;
          vlib_buffer_t * b0, * b1;
          u32 next0, next1;
          u32 sw_if_index0, sw_if_index1;
          
	  /* Prefetch next iteration. */
	  {
	    vlib_buffer_t * p2, * p3;
            
	    p2 = vlib_get_buffer (vm, from[2]);
	    p3 = vlib_get_buffer (vm, from[3]);
            
	    vlib_prefetch_buffer_header (p2, LOAD);
	    vlib_prefetch_buffer_header (p3, LOAD);

	    CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
	    CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
	  }

          /* speculatively enqueue b0 and b1 to the current next frame */
	  to_next[0] = bi0 = from[0];
	  to_next[1] = bi1 = from[1];
	  from += 2;
	  to_next += 2;
	  n_left_from -= 2;
	  n_left_to_next -= 2;

	  b0 = vlib_get_buffer (vm, bi0);
	  b1 = vlib_get_buffer (vm, bi1);

          sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
          next0 = vcm->cached_next_index;
          sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
          next1 = vcm->cached_next_index;

          /* $$$$ your message in this space. Process 2 x pkts */
          em->counters[node_counter_base_index + VCGN_CLASSIFY_ERROR_PACKETS_RECEIVED] += 2;

          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
            {
              if (b0->flags & VLIB_BUFFER_IS_TRACED) 
                {
                    vcgn_classify_trace_t *t = 
                      vlib_add_trace (vm, node, b0, sizeof (*t));
                    t->sw_if_index = sw_if_index0;
                    t->next_index = next0;
                  }
                if (b1->flags & VLIB_BUFFER_IS_TRACED) 
                  {
                    vcgn_classify_trace_t *t = 
                      vlib_add_trace (vm, node, b1, sizeof (*t));
                    t->sw_if_index = sw_if_index1;
                    t->next_index = next1;
                  }
              }
            
            /* verify speculative enqueues, maybe switch current next frame */
            vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
                                             to_next, n_left_to_next,
                                             bi0, bi1, next0, next1);
        }
        #endif /* if 0 */
      
      while (n_left_from > 0 && n_left_to_next > 0)
      {
          u32 bi0;
          vlib_buffer_t * b0;
          u32 next0;
          u32 sw_if_index0;
          ip4_header_t * h0;
          //ipv4_header *h0;
          ethernet_header_t *eth0;
          icmp_v4_t *icmp;
          u8 icmp_type;
          u8 ipv4_hdr_len;

          /* speculatively enqueue b0 to the current next frame */
          bi0 = from[0];
          to_next[0] = bi0;
          from += 1;
          to_next += 1;
          n_left_from -= 1;
          n_left_to_next -= 1;

          b0 = vlib_get_buffer (vm, bi0);
          
          eth0 = (ethernet_header_t *) vlib_buffer_get_current(b0);
          u16 *etype = &eth0->type;
    
          /* vlan tag 0x8100 */      
          if (*etype == clib_host_to_net_u16(ETHERNET_TYPE_VLAN)) { 
            l3_type = (etype + 1); /* Skip 2 bytes of vlan id */  
            vlib_buffer_advance(b0, 18);
          } else {
            l3_type = etype;
            vlib_buffer_advance(b0, 14);
          }
          /* Handling v4 pkts 0x800 */
          if (*l3_type == clib_host_to_net_u16(ETHERNET_TYPE_IP4)) {  
          
              h0 = vlib_buffer_get_current (b0);

              u8 protocol_type = h0->protocol;

              sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
              next0 = VCGN_CLASSIFY_NEXT_IP4_INPUT;
              counter = VCGN_CLASSIFY_ERROR_V4_PACKETS_PROCESSED;

              if (protocol_type == 0x11) { /* UDP# 17 */
                  next0 = (sw_if_index0 < vec_len(vcm->inside_sw_if_index_table) &&
                    vcm->inside_sw_if_index_table[sw_if_index0] != EMPTY) ?
                        VCGN_CLASSIFY_NEXT_UDP_INSIDE : next0;

                  next0 = (sw_if_index0 < vec_len(vcm->outside_sw_if_index_table) &&
                    vcm->outside_sw_if_index_table[sw_if_index0] != EMPTY) ?
                        VCGN_CLASSIFY_NEXT_UDP_OUTSIDE : next0;

              } else if (protocol_type == 0x06) { /* TCP# 6 */
                  next0 = (sw_if_index0 < vec_len(vcm->inside_sw_if_index_table) &&
                    vcm->inside_sw_if_index_table[sw_if_index0] != EMPTY) ?
                        VCGN_CLASSIFY_NEXT_TCP_INSIDE : next0;

                  next0 = (sw_if_index0 < vec_len(vcm->outside_sw_if_index_table) &&
                    vcm->outside_sw_if_index_table[sw_if_index0] != EMPTY) ?
                        VCGN_CLASSIFY_NEXT_TCP_OUTSIDE : next0;

              } else if (protocol_type == 0x01) { /* ICMP # 1 */

                  ipv4_hdr_len = (h0->ip_version_and_header_length & 0xf) << 2;
                  icmp = (icmp_v4_t *)((u8*)h0 + ipv4_hdr_len);
                  icmp_type = icmp->type;

                  if ((icmp_type == ICMPV4_ECHO) || 
                          (icmp_type == ICMPV4_ECHOREPLY)) {
                      next0 = (sw_if_index0 < vec_len(vcm->inside_sw_if_index_table) &&
                        vcm->inside_sw_if_index_table[sw_if_index0] != EMPTY) ?
                            VCGN_CLASSIFY_NEXT_ICMP_Q_INSIDE : next0;

                      next0 = (sw_if_index0 < vec_len(vcm->outside_sw_if_index_table) &&
                        vcm->outside_sw_if_index_table[sw_if_index0] != EMPTY) ?
                            VCGN_CLASSIFY_NEXT_ICMP_Q_OUTSIDE : next0;

                  } else {
                      next0 = (sw_if_index0 < vec_len(vcm->inside_sw_if_index_table) &&
                        vcm->inside_sw_if_index_table[sw_if_index0] != EMPTY) ?
                            VCGN_CLASSIFY_NEXT_ICMP_E_INSIDE : next0;

                      next0 = (sw_if_index0 < vec_len(vcm->outside_sw_if_index_table) &&
                        vcm->outside_sw_if_index_table[sw_if_index0] != EMPTY) ?
                            VCGN_CLASSIFY_NEXT_ICMP_E_OUTSIDE : next0;
                  }
              } else {
                 /* cannot do NATting with this L4 protocol */
                  counter = VCGN_CLASSIFY_ERROR_V4_PACKETS_PUNTED;
              }

              if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
                          && (b0->flags & VLIB_BUFFER_IS_TRACED))) {
                  udp_header_t * u0 = (udp_header_t *)(h0+1);
                  vcgn_classify_trace_t *t = 
                      vlib_add_trace (vm, node, b0, sizeof (*t));
                  t->sw_if_index = sw_if_index0;
                  t->next_index = next0;
                  t->orig_dst_address = h0->dst_address.as_u32;
                  t->orig_dst_port = u0->dst_port;
              }

          } else if (*l3_type == clib_host_to_net_u16(ETHERNET_TYPE_IP6)) { 

                /* IPv6 0x86DD */
                next0 = VCGN_CLASSIFY_NEXT_IP6_INPUT;
                counter = VCGN_CLASSIFY_ERROR_V6_PACKETS_PUNTED;

            } else if (*l3_type == 
                clib_host_to_net_u16(ETHERNET_TYPE_MPLS_UNICAST)) { 

                /* MPLS unicast 0x8847 */
                next0 = VCGN_CLASSIFY_NEXT_MPLS_INPUT;
                counter = VCGN_CLASSIFY_ERROR_MPLS_PACKETS_PUNTED;
          } else { /* Remaining all should be pushed to "ethernet-input" */

                next0 = VCGN_CLASSIFY_NEXT_ETHERNET_INPUT;
                counter = VCGN_CLASSIFY_ERROR_ETH_PACKETS_PUNTED;
          }

          em->counters[node_counter_base_index + counter] += 1;
          em->counters[node_counter_base_index + 
                    VCGN_CLASSIFY_ERROR_PACKETS_RECEIVED] += 1;

          /* verify speculative enqueue, maybe switch current next frame */
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  return frame->n_vectors;
}

VLIB_REGISTER_NODE (vcgn_classify_node) = {
  .function = vcgn_classify_node_fn,
  .name = "vcgn-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_swap_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,
  
  .n_errors = ARRAY_LEN(vcgn_classify_error_strings),
  .error_strings = vcgn_classify_error_strings,

  .n_next_nodes = VCGN_CLASSIFY_N_NEXT,

  /* edit / add dispositions here */
  .next_nodes = {
    [VCGN_CLASSIFY_NEXT_IP4_INPUT]      = "ip4-input",
    [VCGN_CLASSIFY_NEXT_IP6_INPUT]      = "ip6-input",
    [VCGN_CLASSIFY_NEXT_MPLS_INPUT]     = "mpls-gre-input",
    [VCGN_CLASSIFY_NEXT_ETHERNET_INPUT] = "ethernet-input",
	[VCGN_CLASSIFY_NEXT_UDP_INSIDE]     = "vcgn-v4-udp-i2o",
	[VCGN_CLASSIFY_NEXT_UDP_OUTSIDE]    = "vcgn-v4-udp-o2i",
	[VCGN_CLASSIFY_NEXT_TCP_INSIDE]     = "vcgn-v4-tcp-i2o",
	[VCGN_CLASSIFY_NEXT_TCP_OUTSIDE]    = "vcgn-v4-tcp-o2i",
	[VCGN_CLASSIFY_NEXT_ICMP_Q_INSIDE]  = "vcgn-v4-icmp-q-i2o",
	[VCGN_CLASSIFY_NEXT_ICMP_Q_OUTSIDE] = "vcgn-v4-icmp-q-o2i",
	[VCGN_CLASSIFY_NEXT_ICMP_E_INSIDE]  = "vcgn-v4-icmp-e-i2o",
	[VCGN_CLASSIFY_NEXT_ICMP_E_OUTSIDE] = "vcgn-v4-icmp-e-o2i"
  },
};


/* A test function to init the vrf map */

clib_error_t *vcgn_classify_init (vlib_main_t *vm)
{
  vcgn_classify_main_t * mp = &vcgn_classify_main;
    
  mp->vlib_main = vm;
  mp->vnet_main = vnet_get_main();
  u32 inside_sw_if_index = 1;
  u32 outside_sw_if_index = 0;

  vec_validate_init_empty (mp->inside_sw_if_index_table,
    inside_sw_if_index + 1, EMPTY);
  vec_validate_init_empty (mp->outside_sw_if_index_table,
    outside_sw_if_index + 1, EMPTY);

  /*
   * inside_sw_if_index cell of the table stores outside_sw_if_index
   * and vice versa. This is ensurs pair of indices being remembered
   * using one mem-location.
   */
  mp->inside_sw_if_index_table[inside_sw_if_index] = outside_sw_if_index;
  mp->outside_sw_if_index_table[outside_sw_if_index] = inside_sw_if_index;

#if DPDK==1
  dpdk_set_next_node (DPDK_RX_NEXT_IP4_INPUT, "vcgn-classify");
#endif

  {
    pg_node_t * pn;
    pn = pg_get_node (vcgn_classify_node.index);
    pn->unformat_edit = unformat_pg_ip4_header;
  }
  return 0;
}

VLIB_INIT_FUNCTION (vcgn_classify_init);

/* Show command handlers */
static clib_error_t *
show_vcgn_stats_command_fn (vlib_main_t * vm,
                         unformat_input_t * input,
                         vlib_cli_command_t * cmd)
{
    if (cnat_db_init_done) {
        cnat_nat44_handle_show_stats(vm);
    } else {
        vlib_cli_output(vm, "vCGN is not configured !!\n");
    }
    return 0;
}


static clib_error_t *
show_vcgn_config_command_fn (vlib_main_t * vm,
                         unformat_input_t * input,
                         vlib_cli_command_t * cmd)
{
  cnat_nat44_handle_show_config(vm);
  return 0;
}

static clib_error_t *
show_vcgn_inside_translation_command_fn (vlib_main_t * vm,
                         unformat_input_t * input,
                         vlib_cli_command_t * cmd)
{
    vnet_main_t * vnm = vnet_get_main();
    vcgn_classify_main_t * vcm = &vcgn_classify_main;
    spp_api_cnat_v4_show_inside_entry_req_t inside_req;
    u8 *proto; 
    ip4_address_t inside_addr;
    u32 start_port = 1;
    u32 end_port = 65535;
    u32 inside_sw_if_index = EMPTY;
    
    inside_req.start_port = start_port;
    inside_req.end_port = end_port;
    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat(input, "protocol %s", &proto)) { 
            if (!strncmp((char *) proto, "udp", 3)) {
                inside_req.protocol = 1;
            } else if (!strncmp((char *) proto, "tcp", 3)) {
                inside_req.protocol = 2;
            } else {
                inside_req.protocol = 3;
            } 
        } else if (unformat (input, "interface %U", 
		    unformat_vnet_sw_interface, vnm, &inside_sw_if_index)) {
            if (inside_sw_if_index > vec_len(vcm->inside_sw_if_index_table) ||
                vcm->inside_sw_if_index_table[inside_sw_if_index] == EMPTY) {
                    return clib_error_return (0, "Could not find the inside interface");
            }
        } else if (unformat (input, "inside-addr %U", 
                       unformat_ip4_address, &inside_addr)) {
            inside_req.ipv4_addr = clib_net_to_host_u32(inside_addr.as_u32); 
        } else if (unformat(input, "start-port %u", &start_port)) {
            inside_req.start_port = start_port;
        } else if (unformat(input, "end-port %u", &end_port)) {
            inside_req.end_port = end_port;
        } else { break;}
    }
    inside_req.vrf_id = inside_sw_if_index;
    inside_req.flags |= CNAT_TRANSLATION_ENTRY_DYNAMIC; /* as of now only dynamic */  
    inside_req.all_entries = 0; /* we can see it later */
#if DEBUG
    vlib_cli_output(vm, "proto %d, inside-addr 0x%x, start_port %u, "
                "end_port %u, vrf 0x%x\n",
                inside_req.protocol, 
                inside_req.ipv4_addr,
                inside_req.start_port,
                inside_req.end_port,
                inside_sw_if_index);
#endif
    if (cnat_db_init_done) {
        cnat_v4_show_inside_entry_req_t_handler(&inside_req, vm);
    } else {
        vlib_cli_output(vm, "vCGN is not configured !!\n");
    }
    return 0;
}


static clib_error_t *
show_vcgn_outside_translation_command_fn (vlib_main_t * vm,
                         unformat_input_t * input,
                         vlib_cli_command_t * cmd)
{
    void cnat_v4_show_outside_entry_req_t_handler
        (spp_api_cnat_v4_show_outside_entry_req_t *mp, vlib_main_t *vm);
    vnet_main_t * vnm = vnet_get_main();
    vcgn_classify_main_t * vcm = &vcgn_classify_main;
    spp_api_cnat_v4_show_outside_entry_req_t outside_req;
    u8 *proto; 
    ip4_address_t outside_addr;
    u32 start_port = 1;
    u32 end_port = 65535;
    u32 outside_sw_if_index = EMPTY;
    
    
    outside_req.start_port = start_port;
    outside_req.end_port = end_port;
    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat(input, "protocol %s", &proto)) { 
            if (!strncmp((char *) proto, "udp", 3)) {
                outside_req.protocol = 1;
            } else if (!strncmp((char *) proto, "tcp", 3)) {
                outside_req.protocol = 2;
            } else {
                outside_req.protocol = 3;
            } 
        } else if (unformat (input, "interface %U", 
            unformat_vnet_sw_interface, vnm, &outside_sw_if_index)) {
            if (outside_sw_if_index > vec_len(vcm->outside_sw_if_index_table) ||
                vcm->outside_sw_if_index_table[outside_sw_if_index] == EMPTY) {
                    return clib_error_return (0, "Could not find the outside interface");
            }
        } else if (unformat (input, "outside-addr %U", 
                       unformat_ip4_address, &outside_addr)) {
            outside_req.ipv4_addr = clib_net_to_host_u32(outside_addr.as_u32); 
        } else if (unformat(input, "start-port %u", &start_port)) {
            outside_req.start_port = start_port;
        } else if (unformat(input, "end-port %u", &end_port)) {
            outside_req.end_port = end_port;
        } else { break;}
    }
    outside_req.vrf_id = outside_sw_if_index;
    outside_req.flags |= CNAT_TRANSLATION_ENTRY_DYNAMIC; /* as of now only dynamic */  
#if DEBUG
    vlib_cli_output(vm, "proto %d, outside-addr 0x%x, start_port %u, "
                "end_port %u, vrf 0x%x\n",
                outside_req.protocol, 
                outside_req.ipv4_addr,
                outside_req.start_port,
                outside_req.end_port,
                outside_sw_if_index);
#endif
    if (cnat_db_init_done) {
        cnat_v4_show_outside_entry_req_t_handler(&outside_req, vm);
    } else {
        vlib_cli_output(vm, "vCGN is not configured !!\n");
    }
    return 0;
}


/* Config command handlers */
static clib_error_t *
set_vcgn_inside_command_fn (vlib_main_t * vm,
			    unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
  vnet_main_t * vnm = vnet_get_main();
  vcgn_classify_main_t * vcm = &vcgn_classify_main;
  u32 inside_sw_if_index = 1;
  u32 outside_sw_if_index = ~0;
  void cnat_db_v2_init (void );

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
      if (unformat(input, "%U", 
		   unformat_vnet_sw_interface, vnm, &inside_sw_if_index))
    ;
      else if (unformat(input, "outside %U", 
		   unformat_vnet_sw_interface, vnm, &outside_sw_if_index))
	; 
      else break;
    }
    if (inside_sw_if_index == ~0 ||
	outside_sw_if_index == ~0)
      return clib_error_return (0, "unknown input `%U'",
				format_unformat_error, input);

    if (inside_sw_if_index == outside_sw_if_index)
      return clib_error_return (0, "inside and outside interfaces can't be the same...");

    /*
     * Initialize in/out sw_if_index table. Could use
     * non-indexed table to reduce memory. However, this
     * is consulted in vcgn_classify for every packet.
     * Therefore, table is indexed by sw_if_index.
     */
    vec_validate_init_empty (vcm->inside_sw_if_index_table,
        inside_sw_if_index + 1, EMPTY);
    vec_validate_init_empty (vcm->outside_sw_if_index_table,
        outside_sw_if_index + 1, EMPTY);

    /*
     * inside_sw_if_index cell of the table stores outside_sw_if_index
     * and vice versa. This is ensurs pair of indices being remembered
     * using one mem-location.
     */
    vcm->inside_sw_if_index_table[inside_sw_if_index] = outside_sw_if_index;
    vcm->outside_sw_if_index_table[outside_sw_if_index] = inside_sw_if_index;

    if (! vcm->cnat_db_initalized) {
        int i;
        cnat_db_v2_init();
        
        for (i = 0; i < CNAT_MAX_VRFMAP_ENTRIES; i++) {
            vrf_map_array[i] = VRF_MAP_ENTRY_EMPTY;
        }
        /* Turn on the db scanner process */
        cnat_scanner_db_process_turn_on(vm);
        vcm->cnat_db_initalized = 1;
    }
    return 0;
}

static clib_error_t *
set_vcgn_map_command_fn (vlib_main_t * vm,
			 unformat_input_t * input,
			 vlib_cli_command_t * cmd)
{
  vnet_main_t * vnm = vnet_get_main();
  vcgn_classify_main_t * vcm = &vcgn_classify_main;
  ip4_address_t lo, hi;
  spp_api_cnat_v4_add_vrf_map_t map;
  u32 inside_sw_if_index = EMPTY;
  u32 outside_sw_if_index;

  vnet_hw_interface_t *inside_hw_if_index = NULL;
  vnet_hw_interface_t *outside_hw_if_index = NULL;

  if (! unformat(input, "inside %U", 
       unformat_vnet_sw_interface, vnm, &inside_sw_if_index))
    return clib_error_return (0, "unknown input `%U'",
                  format_unformat_error, input);

  if (!unformat (input, "%U", unformat_ip4_address, &lo))
    return clib_error_return (0, "unknown input `%U'",
			      format_unformat_error, input);

  if (unformat (input, "- %U", unformat_ip4_address, &hi))
    ;

  /* $$$$ remember to set i_vrf, i_vrf_id as needed */

  /* Fill the structure spp_api_cnat_v4_add_vrf_map_t & let this API handle it */
  /* i_vrf_id & o_vrf_id are 32-bit & i_vrf, o_vrf are 16 bit */

  if (inside_sw_if_index > vec_len(vcm->inside_sw_if_index_table) ||
    vcm->inside_sw_if_index_table[inside_sw_if_index] == EMPTY) {
      return clib_error_return (0, "Could not find the inside interface");
  }
  outside_sw_if_index = vcm->inside_sw_if_index_table[inside_sw_if_index];

  map.i_vrf_id = inside_sw_if_index; 
  map.o_vrf_id = outside_sw_if_index; 
  map.i_vrf    = inside_sw_if_index;
  map.o_vrf    = outside_sw_if_index;

  map.start_addr[0] = clib_net_to_host_u32(lo.as_u32); 
  map.end_addr[0]   = clib_net_to_host_u32(hi.as_u32); 

  cnat_nat44_add_vrf_map_t_handler(&map, vm);

#if 1
  inside_hw_if_index = vnet_get_sup_hw_interface(vcm->vnet_main, inside_sw_if_index);
  if (inside_hw_if_index) {
    vnet_hw_interface_rx_redirect_to_node(vcm->vnet_main, 
            inside_hw_if_index->hw_if_index, vcgn_classify_node.index);
  }
  outside_hw_if_index = vnet_get_sup_hw_interface(vcm->vnet_main, outside_sw_if_index);
  if (outside_hw_if_index) {
    vnet_hw_interface_rx_redirect_to_node(vcm->vnet_main, 
            outside_hw_if_index->hw_if_index, vcgn_classify_node.index);
  }
#endif
  return 0;
}

static clib_error_t *
set_vcgn_tcp_timeout_command_fn (vlib_main_t * vm,
			    unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
  /*
  vnet_main_t * vnm = vnet_get_main();
  vcgn_classify_main_t * vcm = &vcgn_classify_main;
  */
  u32 act_timeout = 0;
  u32 init_timeout = 0;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat(input, "active %u", &act_timeout)) 
            tcp_active_timeout = act_timeout;
        else if (unformat(input, "init %u", &init_timeout)) 
            tcp_initial_setup_timeout = init_timeout; 
        else break;
    }
    return 0;
}

static clib_error_t *
set_vcgn_udp_timeout_command_fn (vlib_main_t * vm,
			    unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
  /*
  vnet_main_t * vnm = vnet_get_main();
  vcgn_classify_main_t * vcm = &vcgn_classify_main;
  */
  u32 act_timeout = 0;
  u32 init_timeout = 0;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat(input, "active %u", &act_timeout)) 
            udp_act_session_timeout = act_timeout;
        else if (unformat(input, "init %u", &init_timeout)) 
            udp_init_session_timeout = init_timeout; 
        else break;
    }
    return 0;
}


static clib_error_t *
set_vcgn_icmp_timeout_command_fn (vlib_main_t * vm,
			    unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
  /* 
   * vnet_main_t * vnm = vnet_get_main(); 
   * vcgn_classify_main_t * vcm = &vcgn_classify_main;
   */
  u32 timeout = 0;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat(input, "%u", &timeout)) 
            ; 
        else break;
    }
    icmp_session_timeout = timeout;
    return 0;
}


static clib_error_t *
set_vcgn_protocol_default_timeout_command_fn (vlib_main_t * vm,
			    unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
    /*
    vnet_main_t * vnm = vnet_get_main();
    vcgn_classify_main_t * vcm = &vcgn_classify_main;
    */
    u8 *protocol;
    u8 reset = 1; 

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
      if (unformat(input, "%s", &protocol)) 
	;
      else break;
    }
    cnat_nat44_set_protocol_timeout_value(0, 0, protocol, reset, vm);
    return 0;
}

static clib_error_t *
set_vcgn_dynamic_port_start_range_command_fn (vlib_main_t * vm,
			    unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
    /*
    vnet_main_t * vnm = vnet_get_main();
    vcgn_classify_main_t * vcm = &vcgn_classify_main;
    */
    u32 port = 0;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
      if (unformat(input, "%u", &port)) 
	;
      else break;
    }
    if (port != 0 && port > 65535) {
        vlib_cli_output(vm, "Error !! Invalid port\n");
    } else {
        cnat_static_port_range = port;
        vlib_cli_output(vm, "Dynamic Port Range Config Successful !!\n");
    }
    return 0;
}

static clib_error_t *
set_vcgn_port_limit_command_fn (vlib_main_t * vm,
			    unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
    /*
    vnet_main_t * vnm = vnet_get_main();
    vcgn_classify_main_t * vcm = &vcgn_classify_main;
    */
    u32 port = 0;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
      if (unformat(input, "%u", &port)) 
	;
      else break;
    }
    if (port != 0 && port > 65535) {
        vlib_cli_output(vm, "Error !! Invalid port\n");
    } else {
        cnat_main_db_max_ports_per_user = port;
        vlib_cli_output(vm, "Port Limit Config Successful !!\n");
    }
    return 0;
}

static inline void nfv9_init_pkt_sent_data(cnat_nfv9_logging_info_t *nfv9_info)
{
    nfv9_server_info_t *server = nfv9_server_info_pool +
        nfv9_info->server_index;

        /*
         * Reset the pkts_since_last_template and sent_time
         * so that template will be sent next time
         */
        server->last_template_sent_time  = 0;
        server->pkts_since_last_template = 0xffffffff;
}

static inline u16 nfv9_get_max_length_minus_max_record_size(u16 path_mtu)
{
    u16 max_length_minus_max_record_size;
    if(!path_mtu) /* Use default */
        path_mtu = NFV9_DEF_PATH_MTU;

    max_length_minus_max_record_size = path_mtu -
        CNAT_NFV9_DATAFLOW_RECORD_HEADER_LENGTH -
        NFV9_PAD_VALUE -
        CNAT_NFV9_MAX_SINGLE_RECORD_LENGTH; /* Note.. as of now this record
            * requires max number of bytes. If you add more records,
            * this needs to be re-checked */
        if (max_length_minus_max_record_size < CNAT_NFV9_MIN_RECORD_SIZE) {
            max_length_minus_max_record_size = CNAT_NFV9_MIN_RECORD_SIZE;
        }
   return max_length_minus_max_record_size;
}

/* This function finds if the netflow server indicated by
 * new_server_info is already configured for some other instance
 * if yes, it returns the same pointer so that, info sent to the
 * server is consistent. If the server is not found, a new instance
 * is created and returned. If an existing server is used, its refernce
 * count is incrimented (indicating the number of instances using the
 * same server
 */
 /* #define DEBUG_NF_SERVER_CONFIG 1 */
static u16 nfv9_get_server_instance(
    cnat_nfv9_logging_info_t *nfv9_info, nfv9_server_info_t *new_server_info)
{

    /* Check if the instance has a server already and if yes, does it match */
    nfv9_server_info_t *server;
    if(nfv9_info->server_index != EMPTY) {
        server =  nfv9_server_info_pool + nfv9_info->server_index;

        if((server->ipv4_address == new_server_info->ipv4_address) &&
            (server->port == new_server_info->port)) {
            /* Same server.. just check if refresh rate/timeouts are reduced */
#ifdef DEBUG_NF_SERVER_CONFIG
            if(my_instance_number == 1) {
            printf("\n Server match for %x and port %d\n",
                new_server_info->ipv4_address, new_server_info->port);
            }
#endif /* #ifdef DEBUG_NF_SERVER_CONFIG */
            goto adjust_refresh_rate;
        } else { /* The server is being changed */
            server->ref_count--;
#ifdef DEBUG_NF_SERVER_CONFIG
            if(my_instance_number == 1) {
            printf("\n Server change from %x, %d to %x, %d"
                "Ref count %d\n",
                server->ipv4_address,
                server->port,
                new_server_info->ipv4_address, new_server_info->port,
                server->ref_count);
            }
#endif /* #ifdef DEBUG_NF_SERVER_CONFIG */
            if(!server->ref_count) {
                /* Return this server to pool */
#ifdef DEBUG_NF_SERVER_CONFIG
                if(my_instance_number == 1) {
                    PLATFORM_DEBUG_PRINT("Deleting Server %x, %d at %d\n",
                    server->ipv4_address,
                    server->port,
                    nfv9_info->server_index);
                }
#endif /* #ifdef DEBUG_NF_SERVER_CONFIG */
                pool_put(nfv9_server_info_pool, server);
            }
        }
    }

    /* Now check if the server is already present in the pool */
    u8 found = 0;
    server = 0;
    pool_foreach (server, nfv9_server_info_pool, ({
        if ((server->ipv4_address == new_server_info->ipv4_address) &&
            (server->port == new_server_info->port)) {
            server->ref_count++;
            nfv9_info->server_index = server - nfv9_server_info_pool;
            found = 1;
#ifdef DEBUG_NF_SERVER_CONFIG
            if(my_instance_number == 1) {
            printf("Re-using server %x, %d Ref count %d\n",
            server->ipv4_address, server->port, server->ref_count);
            }
#endif /* #ifdef DEBUG_NF_SERVER_CONFIG */
            break;
        }
    }));

    if(!found) {
        /* Create a new one, initialize and return */
        server = 0;
        pool_get(nfv9_server_info_pool, server);
        clib_memcpy(server, new_server_info, sizeof(nfv9_server_info_t));
        server->ref_count = 1;
        nfv9_info->server_index = server - nfv9_server_info_pool;
#ifdef DEBUG_NF_SERVER_CONFIG
        if(my_instance_number == 1) {
        printf("Create new server for at %d %x and port %d\n",
                nfv9_info->server_index,
                new_server_info->ipv4_address, new_server_info->port);
        }
#endif /* #ifdef DEBUG_NF_SERVER_CONFIG */
        return CNAT_SUCCESS;
    }

adjust_refresh_rate:
    if(server->refresh_rate >
        new_server_info->refresh_rate) {
        server->refresh_rate =
            new_server_info->refresh_rate;
#ifdef DEBUG_NF_SERVER_CONFIG
        if(my_instance_number == 1) {
        printf("Reset refresh rate to %d\n",
            server->refresh_rate);
        }
#endif /* #ifdef DEBUG_NF_SERVER_CONFIG */
    }

    if(server->timeout_rate >
        new_server_info->timeout_rate) {
        server->timeout_rate =
        new_server_info->timeout_rate;
#ifdef DEBUG_NF_SERVER_CONFIG
        if(my_instance_number == 1) {
            printf("Reset timeout rate to %d\n",
            server->timeout_rate);
        }
#endif /* #ifdef DEBUG_NF_SERVER_CONFIG */
    }

    return CNAT_SUCCESS;
}
static clib_error_t *
set_vcgn_nfv9_logging_cofig_command_fn (vlib_main_t * vm,
			    unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
    vcgn_classify_main_t * vcm = &vcgn_classify_main;
    spp_api_cnat_v4_config_nfv9_logging_t nfv9_conf;
    ip4_address_t server_addr;
    u32 ip_addr = 0;
    u32 port;
    u32 refresh_rate = 0;
    u32 timeout = 0;
    u32 pmtu = 0;
    u8 enable = 1;
/* vcgn changes start*/
    cnat_nfv9_logging_info_t *my_nfv9_logging_info = NULL;
    cnat_nfv9_logging_info_t *my_nfv9_logging_info_tmp = NULL;
    cnat_vrfmap_t *my_vrfmap = 0, *my_vrfmap_temp = 0; 
    u16           i_vrf = ~0;
    u32           i_vrf_id = ~0;
    u8            found;
    u32 inside_sw_if_index = EMPTY;
    /*
     * Init NFv9 logging info as needed, this will be done only once
     */
    cnat_nfv9_logging_init();

    found = 0;
  
/* vcgn changes end*/
    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat (input, "inside %U", 
               unformat_vnet_sw_interface, &inside_sw_if_index)) {
            /* Do nothing */
        } else if (unformat (input, "server %U", unformat_ip4_address, &server_addr))
            ip_addr = clib_net_to_host_u32(server_addr.as_u32);
        else if (unformat(input, "port %u", &port)) 
        ;
        else if (unformat(input, "refresh-rate %u", &refresh_rate)) 
        ;
        else if (unformat(input, "timeout %u", &timeout)) 
        ;
        else if (unformat(input, "pmtu %u", &pmtu)) 
        ;
        else if (unformat(input, "del")) 
            enable = 0;
        else break;
    }

    if (inside_sw_if_index > vec_len(vcm->inside_sw_if_index_table) ||
        vcm->inside_sw_if_index_table[inside_sw_if_index] == EMPTY) {
            return clib_error_return (0, "Could not find the inside interface");
    }
    i_vrf    = inside_sw_if_index;
    i_vrf_id = inside_sw_if_index; 

    #if 0
    vlib_cli_output(vm, "ip 0x%x, port %u, refresh %u, "
                    "timeout %u, pmtu %u enable %u\n",
                    ip_addr, port, refresh_rate, 
                    timeout, pmtu, enable);
    #endif
    if (refresh_rate == 0) refresh_rate = 500; /* num of pkts */
    if (timeout == 0) timeout = 30;  /* in mins */

    nfv9_conf.enable = enable;
    nfv9_conf.ipv4_address = ip_addr;
    nfv9_conf.i_vrf_id = inside_sw_if_index;
    nfv9_conf.i_vrf = inside_sw_if_index;
    nfv9_conf.port = port;
    nfv9_conf.refresh_rate = refresh_rate;
    nfv9_conf.timeout_rate = timeout;
    nfv9_conf.path_mtu = pmtu;
    nfv9_conf.nfv9_global_collector = 0;
    nfv9_conf.session_logging = 0;

    /*
     * At this point the NFv9 global information should already be
     * inited as we have called cnat_nfv9_logging_init()
     */

    if (nfv9_conf.nfv9_global_collector) {
        if (cnat_nfv9_global_info.cnat_nfv9_global_collector_index != EMPTY) {
            found = 1;
            my_nfv9_logging_info = cnat_nfv9_logging_info_pool +
                    cnat_nfv9_global_info.cnat_nfv9_global_collector_index;
        }
    } else {
        /* Do we already have a map for this VRF? */
        pool_foreach (my_nfv9_logging_info, cnat_nfv9_logging_info_pool, ({
              if (my_nfv9_logging_info->i_vrf_id == i_vrf_id) {
                  nfv9_server_info_t *server =  nfv9_server_info_pool +
                      my_nfv9_logging_info->server_index;
                  if((server->ipv4_address ==  (nfv9_conf.ipv4_address)) && (server->port == (nfv9_conf.port))) {
                      found = 1;
                      my_nfv9_logging_info_tmp = my_nfv9_logging_info;
                      break;
                  }
              }
          }));
    }

    if ((nfv9_conf.ipv4_address == 0) ||
        (nfv9_conf.port == 0)) {
        vlib_cli_output(vm,
            "Add NFv9 ivrf %d Logging Invalid values [IPv4 0x%x, PORT %d]\n",
                i_vrf,
                (nfv9_conf.ipv4_address),
                (nfv9_conf.port));
         goto done;    
    }

    if (nfv9_conf.enable) {
        if ((nfv9_conf.ipv4_address == 0) ||
              (nfv9_conf.port == 0)) {
              nfv9_conf.rc = CNAT_ERR_PARSER;
              vlib_cli_output(vm,
                  "NFV9_logging i_vrf %d, Invalid [v4_addr 0x%x port %d]\n",
                  i_vrf,
                  (nfv9_conf.ipv4_address),
                  (nfv9_conf.port));
              goto done;    
        }

        nfv9_server_info_t new_server_info;
        memset(&new_server_info, 0, sizeof(nfv9_server_info_t));
        new_server_info.ipv4_address =
                nfv9_conf.ipv4_address;
        new_server_info.port =
                (nfv9_conf.port);
        new_server_info.refresh_rate =
            (nfv9_conf.refresh_rate);
        /*
         * Store the timeout in seconds.  User configures it in minutes
         */
        new_server_info.timeout_rate =
            60*(nfv9_conf.timeout_rate);
        if (found && my_nfv9_logging_info) {
            /*
             * Entry already present, change it
             */
            my_nfv9_logging_info->max_length_minus_max_record_size =
                nfv9_get_max_length_minus_max_record_size(
                    ((nfv9_conf.path_mtu)));
        } else {
            pool_get(cnat_nfv9_logging_info_pool, my_nfv9_logging_info);
            memset(my_nfv9_logging_info, 0, sizeof(*my_nfv9_logging_info));
            my_nfv9_logging_info->server_index = EMPTY;
            my_nfv9_logging_info->nfv9_logging_next_index = EMPTY;
            /*
             * Make the current and head logging context indeices as EMPTY.
             * When first logging happens, these get set correctly
             */
            my_nfv9_logging_info->current_logging_context = NULL;
            my_nfv9_logging_info->queued_logging_context  = NULL;
#if 0
            my_nfv9_logging_info->f  = NULL;
            my_nfv9_logging_info->to_next  = NULL;
            output_node =  vlib_get_node_by_name (vm, (u8 *) "ip4-input");
            my_nfv9_logging_info->ip4_input_node_index = output_node->index;
            printf("ip4_input_node_index %d\n", my_nfv9_logging_info->ip4_input_node_index);
#endif
            my_nfv9_logging_info->i_vrf    = i_vrf;
            my_nfv9_logging_info->i_vrf_id = i_vrf_id;
            my_nfv9_logging_info->max_length_minus_max_record_size =
            nfv9_get_max_length_minus_max_record_size(
                    nfv9_conf.path_mtu);

         /* my_nfv9_logging_info will have a copy of logging_policy
          * because, it is quite possible that nfv9 config arrives before
          * the corresponding vrfmap is initialized. In such cases
          * this copy will be used to update the vrfmap entry
          */
            my_nfv9_logging_info->logging_policy = nfv9_conf.session_logging;

            if (nfv9_conf.nfv9_global_collector) {
                cnat_nfv9_global_info.cnat_nfv9_global_collector_index =
                    my_nfv9_logging_info - cnat_nfv9_logging_info_pool;

                pool_foreach (my_vrfmap, cnat_map_by_vrf, ({
                            if (my_vrfmap->nfv9_logging_index == EMPTY) {
                                my_vrfmap->nfv9_logging_index =
                                    cnat_nfv9_global_info.cnat_nfv9_global_collector_index;
                            }
                        }));
            } else {
                u32 my_vrfmap_found = 0;

                FIND_MY_VRF_USING_I_VRF_ID
                my_vrfmap = my_vrfmap_temp;
                    if (my_vrfmap_found) {
                        if(my_vrfmap->nfv9_logging_index == EMPTY) {
                            my_vrfmap->nfv9_logging_index =
                                my_nfv9_logging_info - cnat_nfv9_logging_info_pool;
                            // my_vrfmap->nf_logging_policy = mp->session_logging;
                        } else {
                            cnat_nfv9_logging_info_t *my_nfv9_logging_info_temp = cnat_nfv9_logging_info_pool + my_vrfmap->nfv9_logging_index;
                            while(my_nfv9_logging_info_temp->nfv9_logging_next_index != EMPTY){
                                my_nfv9_logging_info_temp = cnat_nfv9_logging_info_pool + my_nfv9_logging_info_temp->nfv9_logging_next_index;
                            }
                            my_nfv9_logging_info_temp->nfv9_logging_next_index = my_nfv9_logging_info - cnat_nfv9_logging_info_pool;
                        }
                    }
            }
        }

        /* Update logging policy */
        my_nfv9_logging_info->logging_policy = nfv9_conf.session_logging;
        if (nfv9_conf.nfv9_global_collector) {
                if(PLATFORM_DBL_SUPPORT) {
                    pool_foreach (my_vrfmap, cnat_map_by_vrf, ({
                        if (my_vrfmap->nfv9_logging_index ==
                    cnat_nfv9_global_info.cnat_nfv9_global_collector_index) {
                    my_vrfmap->nf_logging_policy = nfv9_conf.session_logging;
                }
                    }));
                } else {
                        nfv9_conf.rc = CNAT_ERR_NO_SESSION_DB;
                }
        } else {
                if(PLATFORM_DBL_SUPPORT) {
                        u32 my_vrfmap_found = 0;
                        my_vrfmap_temp = NULL;
                        FIND_MY_VRF_USING_I_VRF_ID
                        my_vrfmap = my_vrfmap_temp;
                        if (my_vrfmap_found) {
                          //    my_vrfmap->nf_logging_policy = mp->session_logging;
                        }
                } else {
                        nfv9_conf.rc = CNAT_ERR_NO_SESSION_DB;
                }
        }
        u8  nfv9_logging_policy = 0;
        u32 my_vrfmap_found = 0;
        my_vrfmap_temp = NULL;
        FIND_MY_VRF_USING_I_VRF_ID
        my_vrfmap = my_vrfmap_temp;
            if (my_vrfmap_found) {
                u32 index_curr = my_vrfmap->nfv9_logging_index;
                cnat_nfv9_logging_info_t *my_nfv9_logging_info_temp;
                while(index_curr != EMPTY) {
                    my_nfv9_logging_info_temp = cnat_nfv9_logging_info_pool + index_curr;
                    nfv9_logging_policy = nfv9_logging_policy || my_nfv9_logging_info_temp->logging_policy;
                    index_curr = (cnat_nfv9_logging_info_pool + index_curr)->nfv9_logging_next_index;
                }
                my_vrfmap->nf_logging_policy = nfv9_logging_policy;
            }
            //vlib_cli_output(vm,"Netflow logging policy = %d\n", my_vrfmap->nf_logging_policy);
            if(nfv9_get_server_instance(my_nfv9_logging_info, &new_server_info)
               != CNAT_SUCCESS) {
                vlib_cli_output(vm, "Error to get server instance");
                nfv9_conf.rc = CNAT_ERR_PARSER;
                goto done;
            }
        nfv9_init_pkt_sent_data(my_nfv9_logging_info);

        vlib_cli_output(vm,"Adding NFv9 Logging Succeeded\n");
        nfv9_configured = 1;

    } else {
    /*Delete path*/
        if (found) {
            /* if found entry then we need to overwrite the my_nfv9_logging_info_tmp
             * to my_nfv9_logging_info
             */
            my_nfv9_logging_info = my_nfv9_logging_info_tmp;
            if (i_vrf == INVALID_UIDX) {
                /*
                 * We are deleting a global collector.  Mark the collectors
                 * in those VRFs using the global collector
                 */
                pool_foreach (my_vrfmap, cnat_map_by_vrf, ({
                            if (my_vrfmap->nfv9_logging_index ==
                                cnat_nfv9_global_info.cnat_nfv9_global_collector_index) {
                                my_vrfmap->nfv9_logging_index = EMPTY;
                            }
                        }));

                cnat_nfv9_global_info.cnat_nfv9_global_collector_index = EMPTY;
            } else {
                u32 my_vrfmap_found = 0;
                my_vrfmap_temp = NULL;
                FIND_MY_VRF_USING_I_VRF_ID
                my_vrfmap = my_vrfmap_temp;
                    if (my_vrfmap_found) {
                        // my_vrfmap->nfv9_logging_index = cnat_nfv9_global_info.cnat_nfv9_global_collector_index;
                    }
            }
            if (my_nfv9_logging_info->queued_logging_context ||
                my_nfv9_logging_info->current_logging_context) {
                /*
                 * If there is a pending context:
                 * Set the deleted flag to 1.  This will ensure
                 * that the logging info structure gets freed after any
                 * pending packet get sent
                 */
                my_nfv9_logging_info->deleted = 1;
            } else {
                /*
                 * No pending context, just free the logging info structure
                 */
                u32  index = my_nfv9_logging_info - cnat_nfv9_logging_info_pool;
                if(index == my_vrfmap->nfv9_logging_index) {
                    /* Deleting the first sever */
                    my_vrfmap->nfv9_logging_index = my_nfv9_logging_info->nfv9_logging_next_index;
                    /* if(my_nfv9_logging_info->nfv9_logging_next_index != EMPTY){
                        my_vrfmap->nf_logging_policy = (cnat_nfv9_logging_info_pool + my_nfv9_logging_info->nfv9_logging_next_index)->logging_policy;
                    } else {
                        my_vrfmap->nf_logging_policy = EMPTY;
                    }*/
                } else {
                    u32 index_curr = my_vrfmap->nfv9_logging_index;
                    u32 index_prev = EMPTY;
                    while(index_curr != EMPTY) {
                        index_prev = index_curr;
                        index_curr = (cnat_nfv9_logging_info_pool + index_curr)->nfv9_logging_next_index;
                        if(index == index_curr)
                        {
                            (cnat_nfv9_logging_info_pool + index_prev)->nfv9_logging_next_index = (cnat_nfv9_logging_info_pool + index_curr)->nfv9_logging_next_index;
                            break;
                        }
                    }
                }
                nfv9_delete_server_info(my_nfv9_logging_info);
                pool_put(cnat_nfv9_logging_info_pool, my_nfv9_logging_info);
            }

            vlib_cli_output(vm, "Deleting NFv9 Logging Succeeded\n");
            /* 
             * Search across all vrf and check if nfv9 logging is configured.
             */ 
            nfv9_configured = 0;
            pool_foreach (my_nfv9_logging_info, cnat_nfv9_logging_info_pool, ({
                 nfv9_configured = 1;
                 break;
            }));
        } else {
            nfv9_conf.rc = CNAT_NO_CONFIG;
            vlib_cli_output(vm, "Add NFv9 Logging Failed (2) Non Existent vrf %d\n",
                                     i_vrf);

        }
        u8  nfv9_logging_policy = 0;
        u32 my_vrfmap_found = 0;
        my_vrfmap_temp = NULL;
        FIND_MY_VRF_USING_I_VRF_ID
        my_vrfmap = my_vrfmap_temp; 
            if (my_vrfmap_found) {
                u32 index_curr = my_vrfmap->nfv9_logging_index;
                cnat_nfv9_logging_info_t *my_nfv9_logging_info_temp;
                while(index_curr != EMPTY) {
                    my_nfv9_logging_info_temp = cnat_nfv9_logging_info_pool + index_curr;
                    nfv9_logging_policy = nfv9_logging_policy || my_nfv9_logging_info_temp->logging_policy;
                    index_curr = (cnat_nfv9_logging_info_pool + index_curr)->nfv9_logging_next_index;
                }
                my_vrfmap->nf_logging_policy = nfv9_logging_policy;
            }
    }

done:
    return 0;
}

/* config CLIs */
VLIB_CLI_COMMAND (set_vcgn_map_command) = {
    .path = "set vcgn map",
    .short_help = "set vcgn map <lo-address> [- <hi-address>]",
    .function = set_vcgn_map_command_fn,
};

VLIB_CLI_COMMAND (set_vcgn_inside_command) = {
    .path = "set vcgn inside",
    .short_help = "set vcgn inside <inside intfc> outside <outside intfc>",
    .function = set_vcgn_inside_command_fn,
};

VLIB_CLI_COMMAND (set_vcgn_tcp_timeout_command) = {
    .path = "set vcgn tcp timeout",
    .short_help = "set vcgn tcp timeout active <1-65535> init <1-65535>",
    .function = set_vcgn_tcp_timeout_command_fn,
};

VLIB_CLI_COMMAND (set_vcgn_udp_timeout_command) = {
    .path = "set vcgn udp timeout",
    .short_help = "set vcgn udp timeout active <1-65535> init <1-65535>",
    .function = set_vcgn_udp_timeout_command_fn,
};

VLIB_CLI_COMMAND (set_vcgn_icmp_timeout_command) = {
    .path = "set vcgn icmp timeout",
    .short_help = "set vcgn icmp timeout <1-65535>",
    .function = set_vcgn_icmp_timeout_command_fn,
};

VLIB_CLI_COMMAND (set_vcgn_protocol_default_timeout_command) = {
    .path = "set vcgn default timeout",
    .short_help = "set vcgn default timeout protocol <tcp/udp/icmp>",
    .function = set_vcgn_protocol_default_timeout_command_fn,
};

VLIB_CLI_COMMAND (set_vcgn_dynamic_port_start_range_command) = {
    .path = "set vcgn dynamic port start",
    .short_help = "set vcgn dynamic port start <1-65535>",
    .function = set_vcgn_dynamic_port_start_range_command_fn,
};

VLIB_CLI_COMMAND (set_vcgn_port_limit_command) = {
    .path = "set vcgn port limit",
    .short_help = "set vcgn port limit <1-65535>",
    .function = set_vcgn_port_limit_command_fn,
};

VLIB_CLI_COMMAND (set_vcgn_nfv9_logging_cofig_command) = {
    .path = "set vcgn nfv9",
    .short_help = "set vcgn nfv9 [del] inside <interface> "
                  "server <ip-addr> port <port> [refresh-rate <n>] "
                  "[timeout <n>] [pmtu <n>]",
    .function = set_vcgn_nfv9_logging_cofig_command_fn,
};


/* show CLIs */
VLIB_CLI_COMMAND (show_vcgn_config_command) = {
    .path = "show vcgn config",
    .short_help = "show vcgn config",
    .function = show_vcgn_config_command_fn,
};

VLIB_CLI_COMMAND (show_vcgn_stat_command) = {
    .path = "show vcgn statistics",
    .short_help = "show vcgn statistics",
    .function = show_vcgn_stats_command_fn,
};

VLIB_CLI_COMMAND (show_vcgn_inside_translation_command) = {
    .path = "show vcgn inside-translation",
    .short_help = "show vcgn inside-translation protocol <tcp/udp/icmp> "
                  "interface <inside-if> inside-addr <ip-addr> "
                  "[start-port <n>] [end-port <n>]",
    .function = show_vcgn_inside_translation_command_fn,
};

VLIB_CLI_COMMAND (show_vcgn_outside_translation_command) = {
    .path = "show vcgn outside-translation",
    .short_help = "show vcgn outside-translation protocol <tcp/udp/icmp> "
                  "interface <outside-if> outside-addr <ip-addr> "
                  "[start-port <n>] [end-port <n>]",
    .function = show_vcgn_outside_translation_command_fn,
};

static clib_error_t *
vcgn_init (vlib_main_t * vm)
{
  clib_error_t * error = 0;

  if ((error = vlib_call_init_function 
       (vm, vcgn_classify_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_udp_inside_input_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_udp_outside_input_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_udp_inside_input_exc_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_db_scanner_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_tcp_inside_input_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_tcp_inside_input_exc_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_tcp_outside_input_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_icmp_q_inside_input_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_icmp_q_inside_input_exc_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_icmp_q_outside_input_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_icmp_e_inside_input_init)))
    return error;
  if ((error = vlib_call_init_function 
       (vm, cnat_ipv4_icmp_e_outside_input_init)))
    return error;

  return error;
}

/* 
 * This routine exists to convince the vlib plugin framework that
 * we haven't accidentally copied a random .dll into the plugin
 * directory. This is used in lieu of VLIB_INIT_FUNCTION(vcgn_init).
 *
 * Also collects global variable pointers passed from the vpp engine
 */
clib_error_t *
vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
                      int from_early_init)
{
    return vcgn_init(vm);
}
