/*
 *------------------------------------------------------------------
 * cnat_syslog.c
 *
 * Copyright (c) 2011-2013 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 <arpa/inet.h>
#include "cnat_syslog.h"
#include "platform_common.h"
#include "cnat_db.h"
#include "cnat_log_common.h"
#include <vppinfra/pool.h>

#define SYSLOG_DELIMITER    ' '
#define SYSLOG_FIELD_ABSENT '-' 
/* #define SHOW_SYSLOG_TIMESTAMP 1  TO DO. Remove this later */
/* 
 * Defining the below macro here for now. Assumption is, syslog packets 
 * are sent out via same channel as that of NFV9.
 * Has to be overridden if this assumption is false.
 */
#define PLATFORM_SYSLOG_DISP_NODE_IDX PLATFORM_NFV9_DISP_NODE_IDX

cnat_syslog_global_info_t cnat_syslog_global_info;
cnat_syslog_logging_info_t *cnat_syslog_logging_info_pool;
cnat_syslog_global_counters_t   cnat_syslog_global_counter;
extern u32 syslog_debug_flag;

#define CNAT_SYSLOG_DEBUG_CODE 2

#if CNAT_SYSLOG_DEBUG_CODE > 3
#define SYSLOG_COND if(my_instance_number == 0)

#define SYSLOG_DEBUG_PRINTF1(a) SYSLOG_COND printf(a);
#define SYSLOG_DEBUG_PRINTF2(a, b) SYSLOG_COND printf(a, b);
#define SYSLOG_DEBUG_PRINTF3(a, b, c) SYSLOG_COND printf(a, b, c);
#define SYSLOG_DEBUG_PRINTF4(a, b, c, d) SYSLOG_COND printf(a, b, c, d);

#else

#define SYSLOG_DEBUG_PRINTF1(a)
#define SYSLOG_DEBUG_PRINTF2(a, b)
#define SYSLOG_DEBUG_PRINTF3(a, b, c)
#define SYSLOG_DEBUG_PRINTF4(a, b, c, d)

#endif


void syslog_params_show(u32 logging_index)
{
    cnat_syslog_logging_info_t *log_info __attribute__((unused));
    if(logging_index == EMPTY) {
        PLATFORM_DEBUG_PRINT("\nSyslog logging not configured\n");
        return;
    }

    log_info = cnat_syslog_logging_info_pool + logging_index;

    PLATFORM_DEBUG_PRINT("\nSyslog parameters --\n");
    PLATFORM_DEBUG_PRINT("IPV4 address: %x, port %d, max log size %d\n",
        log_info->ipv4_address,
        log_info->port, log_info->max_length_minus_max_record_size);
    PLATFORM_DEBUG_PRINT("Host name: %s, priority %d", 
        log_info->header_hostname, log_info->header_priority);

}

/* Util function to copy a number as ASCII in to a buf in a
 * faster way (should be faster than sprintf)
 */

const unsigned char ascii_numbers[][3] = 
                {   {'0', '0', '0'},
                    {'1', '0', '0'},
                    {'2', '0', '0'},
                    {'3', '0', '0'},
                    {'4', '0', '0'},
                    {'5', '0', '0'},
                    {'6', '0', '0'},
                    {'7', '0', '0'},
                    {'8', '0', '0'},
                    {'9', '0', '0'},
                    {'1', '0', '0'},
                    {'1', '1', '0'},
                    {'1', '2', '0'},
                    {'1', '3', '0'},
                    {'1', '4', '0'},
                    {'1', '5', '0'},
                    {'1', '6', '0'},
                    {'1', '7', '0'},
                    {'1', '8', '0'},
                    {'1', '9', '0'},
                    {'2', '0', '0'},
                    {'2', '1', '0'},
                    {'2', '2', '0'},
                    {'2', '3', '0'},
                    {'2', '4', '0'},
                    {'2', '5', '0'},
                    {'2', '6', '0'},
                    {'2', '7', '0'},
                    {'2', '8', '0'},
                    {'2', '9', '0'},
                    {'3', '0', '0'},
                    {'3', '1', '0'},
                    {'3', '2', '0'},
                    {'3', '3', '0'},
                    {'3', '4', '0'},
                    {'3', '5', '0'},
                    {'3', '6', '0'},
                    {'3', '7', '0'},
                    {'3', '8', '0'},
                    {'3', '9', '0'},
                    {'4', '0', '0'},
                    {'4', '1', '0'},
                    {'4', '2', '0'},
                    {'4', '3', '0'},
                    {'4', '4', '0'},
                    {'4', '5', '0'},
                    {'4', '6', '0'},
                    {'4', '7', '0'},
                    {'4', '8', '0'},
                    {'4', '9', '0'},
                    {'5', '0', '0'},
                    {'5', '1', '0'},
                    {'5', '2', '0'},
                    {'5', '3', '0'},
                    {'5', '4', '0'},
                    {'5', '5', '0'},
                    {'5', '6', '0'},
                    {'5', '7', '0'},
                    {'5', '8', '0'},
                    {'5', '9', '0'},
                    {'6', '0', '0'},
                    {'6', '1', '0'},
                    {'6', '2', '0'},
                    {'6', '3', '0'},
                    {'6', '4', '0'},
                    {'6', '5', '0'},
                    {'6', '6', '0'},
                    {'6', '7', '0'},
                    {'6', '8', '0'},
                    {'6', '9', '0'},
                    {'7', '0', '0'},
                    {'7', '1', '0'},
                    {'7', '2', '0'},
                    {'7', '3', '0'},
                    {'7', '4', '0'},
                    {'7', '5', '0'},
                    {'7', '6', '0'},
                    {'7', '7', '0'},
                    {'7', '8', '0'},
                    {'7', '9', '0'},
                    {'8', '0', '0'},
                    {'8', '1', '0'},
                    {'8', '2', '0'},
                    {'8', '3', '0'},
                    {'8', '4', '0'},
                    {'8', '5', '0'},
                    {'8', '6', '0'},
                    {'8', '7', '0'},
                    {'8', '8', '0'},
                    {'8', '9', '0'},
                    {'9', '0', '0'},
                    {'9', '1', '0'},
                    {'9', '2', '0'},
                    {'9', '3', '0'},
                    {'9', '4', '0'},
                    {'9', '5', '0'},
                    {'9', '6', '0'},
                    {'9', '7', '0'},
                    {'9', '8', '0'},
                    {'9', '9', '0'},
                    {'1', '0', '0'},
                    {'1', '0', '1'},
                    {'1', '0', '2'},
                    {'1', '0', '3'},
                    {'1', '0', '4'},
                    {'1', '0', '5'},
                    {'1', '0', '6'},
                    {'1', '0', '7'},
                    {'1', '0', '8'},
                    {'1', '0', '9'},
                    {'1', '1', '0'},
                    {'1', '1', '1'},
                    {'1', '1', '2'},
                    {'1', '1', '3'},
                    {'1', '1', '4'},
                    {'1', '1', '5'},
                    {'1', '1', '6'},
                    {'1', '1', '7'},
                    {'1', '1', '8'},
                    {'1', '1', '9'},
                    {'1', '2', '0'},
                    {'1', '2', '1'},
                    {'1', '2', '2'},
                    {'1', '2', '3'},
                    {'1', '2', '4'},
                    {'1', '2', '5'},
                    {'1', '2', '6'},
                    {'1', '2', '7'},
                    {'1', '2', '8'},
                    {'1', '2', '9'},
                    {'1', '3', '0'},
                    {'1', '3', '1'},
                    {'1', '3', '2'},
                    {'1', '3', '3'},
                    {'1', '3', '4'},
                    {'1', '3', '5'},
                    {'1', '3', '6'},
                    {'1', '3', '7'},
                    {'1', '3', '8'},
                    {'1', '3', '9'},
                    {'1', '4', '0'},
                    {'1', '4', '1'},
                    {'1', '4', '2'},
                    {'1', '4', '3'},
                    {'1', '4', '4'},
                    {'1', '4', '5'},
                    {'1', '4', '6'},
                    {'1', '4', '7'},
                    {'1', '4', '8'},
                    {'1', '4', '9'},
                    {'1', '5', '0'},
                    {'1', '5', '1'},
                    {'1', '5', '2'},
                    {'1', '5', '3'},
                    {'1', '5', '4'},
                    {'1', '5', '5'},
                    {'1', '5', '6'},
                    {'1', '5', '7'},
                    {'1', '5', '8'},
                    {'1', '5', '9'},
                    {'1', '6', '0'},
                    {'1', '6', '1'},
                    {'1', '6', '2'},
                    {'1', '6', '3'},
                    {'1', '6', '4'},
                    {'1', '6', '5'},
                    {'1', '6', '6'},
                    {'1', '6', '7'},
                    {'1', '6', '8'},
                    {'1', '6', '9'},
                    {'1', '7', '0'},
                    {'1', '7', '1'},
                    {'1', '7', '2'},
                    {'1', '7', '3'},
                    {'1', '7', '4'},
                    {'1', '7', '5'},
                    {'1', '7', '6'},
                    {'1', '7', '7'},
                    {'1', '7', '8'},
                    {'1', '7', '9'},
                    {'1', '8', '0'},
                    {'1', '8', '1'},
                    {'1', '8', '2'},
                    {'1', '8', '3'},
                    {'1', '8', '4'},
                    {'1', '8', '5'},
                    {'1', '8', '6'},
                    {'1', '8', '7'},
                    {'1', '8', '8'},
                    {'1', '8', '9'},
                    {'1', '9', '0'},
                    {'1', '9', '1'},
                    {'1', '9', '2'},
                    {'1', '9', '3'},
                    {'1', '9', '4'},
                    {'1', '9', '5'},
                    {'1', '9', '6'},
                    {'1', '9', '7'},
                    {'1', '9', '8'},
                    {'1', '9', '9'},
                    {'2', '0', '0'},
                    {'2', '0', '1'},
                    {'2', '0', '2'},
                    {'2', '0', '3'},
                    {'2', '0', '4'},
                    {'2', '0', '5'},
                    {'2', '0', '6'},
                    {'2', '0', '7'},
                    {'2', '0', '8'},
                    {'2', '0', '9'},
                    {'2', '1', '0'},
                    {'2', '1', '1'},
                    {'2', '1', '2'},
                    {'2', '1', '3'},
                    {'2', '1', '4'},
                    {'2', '1', '5'},
                    {'2', '1', '6'},
                    {'2', '1', '7'},
                    {'2', '1', '8'},
                    {'2', '1', '9'},
                    {'2', '2', '0'},
                    {'2', '2', '1'},
                    {'2', '2', '2'},
                    {'2', '2', '3'},
                    {'2', '2', '4'},
                    {'2', '2', '5'},
                    {'2', '2', '6'},
                    {'2', '2', '7'},
                    {'2', '2', '8'},
                    {'2', '2', '9'},
                    {'2', '3', '0'},
                    {'2', '3', '1'},
                    {'2', '3', '2'},
                    {'2', '3', '3'},
                    {'2', '3', '4'},
                    {'2', '3', '5'},
                    {'2', '3', '6'},
                    {'2', '3', '7'},
                    {'2', '3', '8'},
                    {'2', '3', '9'},
                    {'2', '4', '0'},
                    {'2', '4', '1'},
                    {'2', '4', '2'},
                    {'2', '4', '3'},
                    {'2', '4', '4'},
                    {'2', '4', '5'},
                    {'2', '4', '6'},
                    {'2', '4', '7'},
                    {'2', '4', '8'},
                    {'2', '4', '9'},
                    {'2', '5', '0'},
                    {'2', '5', '1'},
                    {'2', '5', '2'},
                    {'2', '5', '3'},
                    {'2', '5', '4'},
                    {'2', '5', '5'}
            };

inline static int 
byte_to_ascii_decimal_unaligned(
    unsigned char *ptr, unsigned char num)
{
    *ptr++ = ascii_numbers[num][0];
    if(PREDICT_FALSE(num < 10)) {
        return 1;
    }
    *ptr++ = ascii_numbers[num][1];
    if(PREDICT_FALSE(num < 100)) {
        return 2;
    }
    *ptr++ = ascii_numbers[num][2];
    return 3; 
}

/* Copies the dotted decimal format of ipv4 
 * in to the space provided and 
 * returns the number of bytes copied
 */
inline static int __attribute__((unused))
copy_ipv4_addr(unsigned char *ptr, u32 ipv4) 
{
    unsigned char *temp = ptr; 
    temp += byte_to_ascii_decimal_unaligned(temp, (ipv4 >> 24));
    *temp++ = '.';
    temp += byte_to_ascii_decimal_unaligned(temp, ((ipv4 >> 16) & 0xFF));
    *temp++ = '.';
    temp += byte_to_ascii_decimal_unaligned(temp, ((ipv4 >> 8) & 0xFF));
    *temp++ = '.';
    temp += byte_to_ascii_decimal_unaligned(temp, (ipv4 & 0xFF));

    return (temp - ptr);
}

#ifdef TOBE_PORTED
/*
 * edt: * * cnat_syslog_fill_ip_header
 *
 * Tries to fill the fields of the IP header before it
 * is sent to the L3 infra node.
 *
 * Argument: cnat_syslog_logging_info_t *logging_info
 * structure that contains the packet context
 */
inline
void cnat_syslog_fill_ip_header (cnat_syslog_logging_info_t *logging_info)
{
    spp_ctx_t      *ctx;
    
    /*
     * Fill in the IP header and port number of the Netflow collector
     * The L3 Infra node will fill in the rest of the fields
     */
    ctx = logging_info->current_logging_context;
    fill_ip_n_udp_hdr(ctx, logging_info->ipv4_address,
        logging_info->port, logging_info->pkt_length);
        
}
#else
inline
void cnat_syslog_fill_ip_header (cnat_syslog_logging_info_t *logging_info)
{
    return;
}
#endif

#ifndef TOBE_PORTED
void cnat_syslog_logging_init()
{
    return;
}

void cnat_syslog_log_mapping_create(cnat_main_db_entry_t * db,
                                  cnat_vrfmap_t *vrfmap)
{
    return;
}

void cnat_syslog_log_mapping_delete(cnat_main_db_entry_t * db,
                                  cnat_vrfmap_t *vrfmap)
{
    return;
}

void cnat_syslog_ds_lite_port_limit_exceeded(
   dslite_key_t   * key,
   dslite_table_entry_t *dslite_entry)
{
    return;
}

void cnat_syslog_nat44_mapping_create(cnat_main_db_entry_t *db,
        cnat_vrfmap_t *vrfmap, cnat_session_entry_t * sdb
#ifndef NO_BULK_LOGGING
                       , int bulk_alloc
#endif
                       )
{
    return;
}

/* Following are in cnat_util.c which are not ported */
/* This function is defined in cnat_util.c which need to be ported */
cnat_icmp_msg_t icmp_msg_gen_allowed ()
{
    return 1;
}

void cnat_syslog_nat44_mapping_delete(cnat_main_db_entry_t *db,
        cnat_vrfmap_t *vrfmap, cnat_session_entry_t *sdb
#ifndef NO_BULK_LOGGING
        , int bulk_alloc
#endif
        )
{
    return;
}

u32
cnat_get_unix_time_in_seconds (void)
{
    return 0;
}
#else /* TOBE_PORTED */
void
cnat_syslog_dump_logging_context (u32 value1, 
                                cnat_syslog_logging_info_t *logging_info,
				u32 value2)
{
    u8 *pkt_ptr;
    u32 i;

    if (PREDICT_TRUE(syslog_debug_flag == 0)) {
        return;
    }
    /*
     * Reduce the logging to few cores, to enable easier debugging
     */
    if ((my_instance_number & 0x7) != 0) {
        return;
    }
    printf("\nDumping %s packet at locn %d: time 0x%x", 
	    (value2 == 1) ? "CURRENT" : "QUEUED",
            value1,
	    cnat_get_unix_time_in_seconds());

    printf("\ni_vrf 0x%x, ip_address 0x%x, port %d, pkt len %d",
             0 /* TO DP Add vrf like nfv9_logging_info->i_vrf */,
             logging_info->ipv4_address,
             logging_info->port,
             logging_info->pkt_length);
    printf("\n");

    if (value2 == 1) {
        pkt_ptr = logging_info->current_logging_context->packet_data;
    } else {
        pkt_ptr = logging_info->queued_logging_context->packet_data;
    }
 
    /*
     * Dump along with 8 bytes of SHIM header
     */
    for (i = 0; i < 
        (logging_info->pkt_length + CNAT_NFV9_IP_HDR_OFFSET); 
         i = i + 1) {
	u8 c1, c2, c3;

	if (i == 0) {
	    printf("\nL2_HEADER + SHIM_HEADER: \n");
	} else if (i == CNAT_NFV9_IP_HDR_OFFSET) {
	    printf("\nIP_HEADER: \n");
	} else if (i == CNAT_NFV9_UDP_HDR_OFFSET) {
	    printf("\nUDP_HEADER: \n");
	} else if (i == CNAT_NFV9_HDR_OFFSET) {
        printf("\nSyslog content..\n");
        while(i < 
            (logging_info->pkt_length + CNAT_NFV9_HDR_OFFSET)) {
            printf("%c", (u8)(*(pkt_ptr + i)));
            i++;
            if((u8)(*(pkt_ptr + i)) == '[') /* new record begins */
                printf("\n");
        }
        return;
    }

    c3 = *(pkt_ptr + i);
    c2 = c3 & 0xf;
    c1 = (c3 >> 4) & 0xf;
    
    printf("%c%c ",
        ((c1 <= 9) ? (c1 + '0') : (c1 - 10 + 'a')),
        ((c2 <= 9) ? (c2 + '0') : (c2 - 10 + 'a')));
        
    }

    printf("\n");
}


/*
 * edt: * * cnat_syslog_send_pkt
 *
 * Tries to send a logging pkt.  If the packet cannot be sent
 * because of rewrite_output node cannot process it, queue
 * it temporarily and try to send it later.
 *
 * Argument: cnat_syslog_logging_info_t *logging_info
 * structure that contains the packet context
 */
inline
void cnat_syslog_send_pkt (cnat_syslog_logging_info_t *logging_info)
{
    spp_node_t                  *output_node;

    cnat_syslog_fill_ip_header(logging_info);

    output_node = spp_get_nodes() + 
	    cnat_syslog_global_info.cnat_syslog_disp_node_index;

    cnat_syslog_dump_logging_context (2, logging_info, 1);

    if (PREDICT_TRUE(output_node->sf.nused < SPP_MAXDISPATCH)) {
        /*
         * Move the logging context to output node
         */
        logging_info->current_logging_context->current_length =
                                 logging_info->pkt_length;
        PLATFORM_SET_CTX_RU_TX_FROM_NODE(logging_info->current_logging_context, \
                                NODE_LOGGING);
        spp_dispatch_make_node_runnable(output_node);
        output_node->sf.ctxs[output_node->sf.nused++] = 
            logging_info->current_logging_context;

        if(PREDICT_FALSE(syslog_debug_flag > 10))
            printf("\nSyslog: 2. Sending Current packet\n");
    } else {
	    /*
	     * Queue the context into the logging_info structure,
	     * We will try to send it later.  Currently, we will
         * restrict to only one context queued.
	     */
        cnat_syslog_global_counter.downstream_constipation_count++;
        if(PREDICT_FALSE(syslog_debug_flag > 10)) 
            printf("\nSyslog: 2. Downstream congestion \n");

	    /*
	     * Attach the current logging context which is full to the
	     * queued context list in logging_info structure
	     */
	    logging_info->queued_logging_context = 
            logging_info->current_logging_context;
    }

    /*
     * Whether the context is queued or not, set the current context index
     * to EMPTY, as the earlier context can no more be used to send
     * more logging records.
     */
    logging_info->current_logging_context = NULL;
}


/*
 * edt: * * cnat_syslog_send_queued_pkt
 *
 * Tries to send a logging pkt that has been queued earlier
 * because it could not be sent due to downstream constipation
 *
 * Argument: cnat_syslog_logging_info_t *logging_info
 * structure that contains the packet context
 */
inline
void cnat_syslog_send_queued_pkt (cnat_syslog_logging_info_t *logging_info)
{
    spp_node_t                  *output_node;

    output_node = spp_get_nodes() + 
	    cnat_syslog_global_info.cnat_syslog_disp_node_index;

    cnat_syslog_dump_logging_context(1, logging_info, 2);

    if(PREDICT_TRUE(output_node->sf.nused < SPP_MAXDISPATCH)) {
        /*
         * Move the logging context to output node
         */
         /** This looks like a bug to me .. need to confirm *****
        logging_info->queued_logging_context->current_length = 
                                 nfv9_logging_info->pkt_length; ***/
        PLATFORM_SET_CTX_RU_TX_FROM_NODE(logging_info->queued_logging_context, 
                                NODE_LOGGING)
        spp_dispatch_make_node_runnable(output_node);
        output_node->sf.ctxs[output_node->sf.nused++] = 
            logging_info->queued_logging_context;

        SYSLOG_DEBUG_PRINTF1("\nSYSLOG: 1. Sending Queued packet\n")

        /*
         * Context has been queued, it will be freed after the pkt
         * is sent.  Clear this from the logging_context_info structure
         */
	    logging_info->queued_logging_context = NULL;

    } else {
        cnat_syslog_global_counter.downstream_constipation_count++;
    }
}

/*
 * edt: * * handle_pending_syslog_pkts
 *
 * Timer handler for sending any pending syslog record
 *
 */
inline
void handle_pending_syslog_pkts()
{
    spp_node_t *output_node;
    cnat_syslog_logging_info_t *my_logging_info = 0;
    u32 current_timestamp = cnat_get_sys_up_time_in_ms();
    i16 sf_nused;
    
    output_node = spp_get_nodes() + 
	    cnat_syslog_global_info.cnat_syslog_disp_node_index;

    sf_nused = output_node->sf.nused;
   
    pool_foreach (my_logging_info, cnat_syslog_logging_info_pool, ({
        /*
         * Check if no more logging contexts can be queued
         */
	if (PREDICT_FALSE(sf_nused >= SPP_MAXDISPATCH)) {
	    break;
	}
    if (my_logging_info->queued_logging_context)
        cnat_syslog_send_queued_pkt (my_logging_info);

    if(my_logging_info->current_logging_context &&
             ((current_timestamp - 
              my_logging_info->current_logging_context_timestamp) 
             > 1000)) {
        /*
         * If there is a current logging context and timestamp
         * indicates it is pending for long, send it out
         * Also if there is a queued context send it out as well
         */
	    SYSLOG_DEBUG_PRINTF4("\nLOG_TIMER: queued %p, curr %p, sf_nused %d",
			       my_logging_info->queued_logging_context, 
			       my_logging_info->current_logging_context, 
			       sf_nused);
        cnat_syslog_send_pkt(my_logging_info);
    }        
    }));
}

const unsigned char hex_numbers_single_digit[] = 
    { '0', '1', '2', '3', '4', '5', '6', '7', '8', 
        '9', 'a', 'b', 'c', 'd', 'e', 'f' };

inline static int u16_to_ascii_decimal_aligned(
    unsigned char *ptr, u16 num, u16 min_digits)
{
    /* The logic below is replicated in 
     * function u16_to_ascii_decimal_unaligned 
     * except the use of min_digits
     * Replication is done to optimize run time
     * if you fix a bug here, check u16_to_ascii_decimal_unaligned
     * as well (and vice versa)
     */
    unsigned char *temp = ptr; 
    int no_leading_zeros = 0;

    if(num > 9999 || min_digits == 5) {
        *temp++ = hex_numbers_single_digit[num/10000];
        num = num%10000;
        no_leading_zeros = 1;
    }
    
    if(no_leading_zeros || num > 999 || min_digits == 4) {
        *temp++ = hex_numbers_single_digit[num/1000];
        num = num%1000;
        no_leading_zeros = 1;
    }

    if(no_leading_zeros || num > 99 || min_digits == 3) {
        *temp++ = hex_numbers_single_digit[num/100];
        num = num%100;
        no_leading_zeros = 1;
    }

    if(no_leading_zeros || num > 9 || min_digits == 2) {
        *temp++ = hex_numbers_single_digit[num/10];
        num = num%10;
    }

    *temp++ = hex_numbers_single_digit[num];
    
    return temp-ptr;
}

inline static int u16_to_ascii_decimal_unaligned(
    unsigned char *ptr, u16 num)
{
    /* 
     * return u16_to_ascii_decimal_aligned(ptr, num, 0);
     * should do the job.. however, to opimize the run time
     * the code of u16_to_ascii_decimal_aligned is being
     * repeated here without the use of min_digits
     * if you fix a bug here, please check 
     * u16_to_ascii_decimal_aligned as well (and vice versa)
     */
    unsigned char *temp = ptr; 
    int no_leading_zeros = 0;

    if(num > 9999) {
        *temp++ = hex_numbers_single_digit[num/10000];
        num = num%10000;
        no_leading_zeros = 1;
    }
    
    if(no_leading_zeros || num > 999) {
        *temp++ = hex_numbers_single_digit[num/1000];
        num = num%1000;
        no_leading_zeros = 1;
    }

    if(no_leading_zeros || num > 99) {
        *temp++ = hex_numbers_single_digit[num/100];
        num = num%100;
        no_leading_zeros = 1;
    }

    if(no_leading_zeros || num > 9) {
        *temp++ = hex_numbers_single_digit[num/10];
        num = num%10;
    }

    *temp++ = hex_numbers_single_digit[num];
    
    return temp-ptr;
}

static int syslog_get_timestamp(unsigned char *ts)
{
    static const char *months[] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ",
            "Jun ", "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec " };

    unsigned char *temp = ts;
    /* Inserts time stamp in the syslog format and returns lenght
     * assumes that ts has sufficient space
     */
    /* China Telecom has demanded that the time stamp has to be 
     * in the format '2011 Jun 7 12:34:08'
     */
    time_t time = (time_t)cnat_get_unix_time_in_seconds();
    struct tm tm1;

    gmtime_r(&time, &tm1);
    /* Now put the pieces together */
    /* Year */
    ts += u16_to_ascii_decimal_unaligned(ts, (tm1.tm_year + 1900));
    *ts++ = SYSLOG_DELIMITER;
    /* Month */
    clib_memcpy(ts, months[tm1.tm_mon], 4);
    ts += 4; /* DELIMITER taken care */
    /* day */
    ts += u16_to_ascii_decimal_unaligned(ts, tm1.tm_mday);
    *ts++ = SYSLOG_DELIMITER;
    /* hours */ 
    ts += u16_to_ascii_decimal_aligned(ts, tm1.tm_hour, 2);
    *ts++ = ':';
    /* minutes */ 
    ts += u16_to_ascii_decimal_aligned(ts, tm1.tm_min, 2);
    *ts++ = ':';
    /* seconds */ 
    ts += u16_to_ascii_decimal_aligned(ts, tm1.tm_sec, 2);
    return ts - temp;
}

/* Ensure that the order of the below array matches with 
 * syslog_service_type enum
 */
static char *syslog_service_string[] = { "NAT44", "DSLITE" };

/* Ensure that the order of below array matches with 
 * syslog_event_type_t enum
 */
typedef struct {
    char *event_name;
    int name_length;
} syslog_event_description_type;

const static syslog_event_description_type sys_log_event[] = { 
    { "UserbasedA", 10 }, /* yes, 10 is strlen of "UserbasedA" */
    { "UserbasedW", 10 },
    { "SessionbasedA", 13 },
    { "SessionbasedW", 13 },
    { "SessionbasedAD", 14 },
    { "SessionbasedWD", 14 },
    { "Portblockrunout", 15 },
    { "TCPseqmismatch", 14},
    { "Invalid", 7 }
};

inline static int syslog_fill_header(const cnat_syslog_logging_info_t *log_info,
        syslog_service_type_t s_type)
{
    /* Forms the syslog header and returns the lenght 
     * Assumes that header has sufficient space 
     */

    /* Sample header (as agreed for China Telecom requirements  --
     * <134> 1 2011 May 31 10:30:45 192.168.2.3 - -   NAT44 -  
     */

    unsigned char *temp, *header;
    int count;
    temp = header = (unsigned char *)
        &(log_info->current_logging_context->packet_data[CNAT_NFV9_HDR_OFFSET]);
    *temp++ = '<';
    temp += byte_to_ascii_decimal_unaligned(temp, 
        log_info->header_priority);
    *temp++ = '>';
    *temp++ = SYSLOG_DELIMITER;
    *temp++ = '1'; /* Syslog version -- always set to 1 */
    *temp++ = SYSLOG_DELIMITER;
    temp += syslog_get_timestamp(temp);
    *temp++ = SYSLOG_DELIMITER;
    count = strlen(log_info->header_hostname);
    clib_memcpy(temp, log_info->header_hostname, count);
    temp += count;
    *temp++ = SYSLOG_DELIMITER;
    *temp++ = SYSLOG_FIELD_ABSENT; /* App name - nil value */
    *temp++ = SYSLOG_DELIMITER;
    *temp++ = SYSLOG_FIELD_ABSENT; /* Proc ID - nil value for now */
    *temp++ = SYSLOG_DELIMITER;
    /* Now the msg id */
    count = strlen(syslog_service_string[s_type]);
    clib_memcpy(temp, syslog_service_string[s_type], count);
    temp += count;
    *temp++ = SYSLOG_DELIMITER;
    *temp++ = SYSLOG_FIELD_ABSENT; /* No structured elements */
    *temp++ = SYSLOG_DELIMITER;
#ifdef SHOW_SYSLOG_TIMESTAMP
    printf("\nSysLog TS: %s : Length %d", header, temp - header);
#endif /*  SHOW_SYSLOG_TIMESTAMP */
    return temp-header;
}

extern void cnat_logging_init();

/* one time call at the beginning */
void cnat_syslog_logging_init() 
{
    if(PREDICT_TRUE(cnat_syslog_global_info.cnat_syslog_init_done))
        return; /* Already done */

    cnat_logging_init();
    cnat_syslog_global_info.cnat_syslog_disp_node_index = 
        spp_lookup_node_index(PLATFORM_SYSLOG_DISP_NODE_IDX);
    ASSERT(cnat_syslog_global_info.cnat_syslog_disp_node_index != (u16)~0);

    cnat_syslog_global_info.cnat_syslog_init_done = 1;
}

/*
 * edt: * * cnat_syslog_create_logging_context
 *
 * Tries to create a logging context with packet buffer
 * to send a new logging packet
 *
 * Argument: cnat_syslog_logging_info_t *logging_info
 * structure that contains the logging info and will store
 * the packet context as well.
 */
inline
void cnat_syslog_create_logging_context (
     cnat_syslog_logging_info_t      *logging_info,
     syslog_service_type_t s_type)
{
    spp_ctx_t *ctx;

    /*
     * If queued_logging_context_index is non-EMPTY, we already have a logging
     * packet queued to be sent.  First try sending this before allocating
     * a new context.  We can have only one active packet context per
     * logging_info structure
     */

    if (PREDICT_FALSE(logging_info->queued_logging_context != NULL)) {
        cnat_syslog_send_queued_pkt(logging_info);
        /*
         * If we cannot still send the queued pkt, just return 
         * Downstream Constipation count would have increased anyway
         */
        if (logging_info->queued_logging_context != NULL) {
	        cnat_syslog_global_counter.logging_context_creation_deferred_count++;
	    return;
        }
    }

    /*
     * If no context can be allocated, return silently
     * calling routine will handle updating the error counters
     */
    if (spp_ctx_alloc(&ctx, 1) < 1) {
        cnat_syslog_global_counter.logging_context_creation_fail_count++;
        SYSLOG_DEBUG_PRINTF1("\nCould not allocate ctx for syslog");
        return;
    }

    // Allocate packet buffer (used for AVSM currently)
    PLATFORM_ALLOC_NFV9_PKT_BUFFER(ctx, 0); 

    logging_info->current_logging_context = ctx;

    PLATFORM_SET_CTX_RU_TX_FROM_NODE(ctx, NODE_LOGGING);

    ctx->flags = SPP_CTX_END_OF_PACKET;
    ctx->next_ctx_this_packet = (spp_ctx_t*) SPP_CTX_NO_NEXT_CTX;
    ctx->current_header = &ctx->packet_data[CNAT_NFV9_HDR_OFFSET];

    logging_info->pkt_length = syslog_fill_header(logging_info, s_type);
    logging_info->pkt_length += (CNAT_NFV9_HDR_OFFSET - 
                                    CNAT_NFV9_IP_HDR_OFFSET); 
    logging_info->current_logging_context_timestamp =
        cnat_get_sys_up_time_in_ms();

}

inline static int u16_to_ascii_hex_unaligned(
    unsigned char *ptr, u16 num)
{
    unsigned char nibble, *temp;
    int no_leading_zeros = 0;
    temp = ptr;
    nibble = (num >> 12);
    if(nibble) {
        *temp++ = hex_numbers_single_digit[nibble];
        no_leading_zeros = 1;
    }

    nibble = (num >> 8) & 0xF;
    if(nibble || no_leading_zeros) {
        *temp++ = hex_numbers_single_digit[nibble];
        no_leading_zeros = 1;
    }
    
    nibble = (num >> 4) & 0xF;
    if(nibble || no_leading_zeros) {
        *temp++ = hex_numbers_single_digit[nibble];
    }
    
    *temp++ = hex_numbers_single_digit[num & 0xF]; 

    return temp-ptr;
}

inline static int ipv6_int_2_str(u32 ipv6[], unsigned char *ipv6_str)
{
/* DC stands for Double Colon.
 * Refer http://tools.ietf.org/html/rfc5952 for 
 * more details on text representations of 
 * IPV6 address
 */
#define DC_NOT_USED_YET 0
#define DC_IN_USE       1 /* Zeros are skipped */
#define DC_ALREADY_USED 2 /* Cannot skip zeros anymore */
    int i;
    u16 *ipv6_temp = (u16 *)ipv6;
    unsigned char *temp = ipv6_str;
    int double_colon = DC_NOT_USED_YET;
    for(i = 0; i < 7; i++) {
        if(ipv6_temp[i]) {
            ipv6_str += u16_to_ascii_hex_unaligned(ipv6_str, ipv6_temp[i]);
            *ipv6_str++ = ':';
            if(double_colon == DC_IN_USE) { /* Cannot use DC anymore */
                double_colon = DC_ALREADY_USED;
            }
        } else {
            if(double_colon == DC_IN_USE) {
                /* Skip this zero as well */
                continue;
            } else if((ipv6_temp[i+1]) 
            /* DC makes sense if there is more than one contiguous zero */
                || (double_colon != DC_NOT_USED_YET)) {
                ipv6_str += u16_to_ascii_hex_unaligned(ipv6_str, 
                        ipv6_temp[i]);
                *ipv6_str++ = ':';
            } else  { /* Start using DC */
                *ipv6_str++ = ':'; /* The 2nd colon */
                double_colon = DC_IN_USE;
            }                
        }                
    }
    if(ipv6_temp[7]) {
        ipv6_str += u16_to_ascii_hex_unaligned(ipv6_str, ipv6_temp[7]);
    } else if(double_colon != DC_IN_USE) {
        *ipv6_str++ = '0';
    }
    *ipv6_str = 0;

    return ipv6_str - temp;
}

/* insert syslog record for nat44 */

void cnat_syslog_insert_nat44_record(
    cnat_syslog_logging_info_t *log_info,
    cnat_main_db_entry_t *db, cnat_vrfmap_t *vrfmap,
    cnat_session_entry_t *sdb, int bulk_alloc, syslog_event_type_t e_type)
{
    /* This record should like this - 
     * [EventName <L4> <Original Source IP> <Inside VRF Name> 
     *   <Original Source IPv6> < Translated Source IP> <Original Port> 
     *   <Translated First Source Port> <Translated Last Source Port>
     *   <Destination ip address> <destination port>]
     */
    u32 original_source = db->in2out_key.k.ipv4;
    u32 translated_ip = db->out2in_key.k.ipv4;
    cnat_user_db_entry_t  *udb = cnat_user_db + db->user_index;
    unsigned char *temp, *record;  
    u32 network_order_ipv6[4];

    SYSLOG_CONFIG_DEBUG_PRINTF(4,"In Function %s\n", __func__);
    temp = record =  &(log_info->current_logging_context->packet_data[
            CNAT_NFV9_IP_HDR_OFFSET + log_info->pkt_length]);

    if (PREDICT_FALSE(!udb)) {
        SYSLOG_DEBUG_PRINTF1("\nnull udb!");
        return;
    }

    /* Now we point to the location where record needs to be inserted */
    *record++ = '['; /* Open the record */

    /* Copy the record type */
    clib_memcpy(record, sys_log_event[e_type].event_name, 
        sys_log_event[e_type].name_length);
    record += sys_log_event[e_type].name_length;
    *record++ = SYSLOG_DELIMITER;
   
    /* Copy the Protocol type */
    if(PREDICT_FALSE(
        e_type == sessionbased_assign || e_type == sessionbased_withdraw ||
        e_type == sessionbased_assignD || e_type == sessionbased_withdrawD)) {
        u16 my_proto_mask;
        my_proto_mask = db->in2out_key.k.vrf & CNAT_PRO_MASK;
        if(PREDICT_TRUE(my_proto_mask == CNAT_TCP)) {
            *record++ = '6';
        } else if(PREDICT_TRUE(my_proto_mask == CNAT_UDP)) {
            *record++ = '1'; 
            *record++ = '7'; 
        } else if(PREDICT_TRUE(my_proto_mask == CNAT_ICMP)) {
            *record++ = '1';
        } else { /* Default, assume GRE (for PPTP) */
            *record++ = '4';
            *record++ = '7';
        }
    } else {
        *record++ = SYSLOG_FIELD_ABSENT;        
    }
    *record++ = SYSLOG_DELIMITER;

    /* Copy the Original Source IP */
    record += copy_ipv4_addr(record, original_source);
    *record++ = SYSLOG_DELIMITER;

    /* copy configured VRF NAME */
    clib_memcpy(record, log_info->vrf_name, log_info->vrf_name_len);
    record += log_info->vrf_name_len;
    *record++ = SYSLOG_DELIMITER;
    
    /* No IPV6 source address for nat44 */
    *record++ = SYSLOG_FIELD_ABSENT;        
    *record++ = SYSLOG_DELIMITER;

    /* Copy the translated IP address */
    record += copy_ipv4_addr(record, translated_ip);
    *record++ = SYSLOG_DELIMITER;
    
    /* Copy the Original port */
    if(e_type == sessionbased_assign || e_type == sessionbased_withdraw ||
       e_type == sessionbased_assignD || e_type == sessionbased_withdrawD) {
        record += u16_to_ascii_decimal_unaligned(
        record, db->in2out_key.k.port); 
    } else {
       *record++ = SYSLOG_FIELD_ABSENT; 
    }
    *record++ = SYSLOG_DELIMITER;

    /* Copy the start outside port */
    record += u16_to_ascii_decimal_unaligned(record, bulk_alloc);
    *record++ = SYSLOG_DELIMITER;

    /* Copy the last outside port */
    if(e_type == userbased_assign || e_type == userbased_withdraw) {
       record += u16_to_ascii_decimal_unaligned(record,
            (bulk_alloc + BULKSIZE_FROM_VRFMAP(vrfmap) - 1));
    } else {
        *record++ = SYSLOG_FIELD_ABSENT;
    }
    *record++ = SYSLOG_DELIMITER;

    /* Copy destination ip and port in case for DBL*/
    if(PREDICT_FALSE(e_type == sessionbased_assignD || e_type == sessionbased_withdrawD)) {
        if(PREDICT_TRUE(sdb == NULL)) {
            record += copy_ipv4_addr(record,db->dst_ipv4);
            *record++ = SYSLOG_DELIMITER;
            record += u16_to_ascii_decimal_unaligned(record, db->dst_port);
        } else {
            record += copy_ipv4_addr(record, sdb->v4_dest_key.k.ipv4);
            *record++ = SYSLOG_DELIMITER;
            record += u16_to_ascii_decimal_unaligned(record, sdb->v4_dest_key.k.port);
        }
    } else {
        *record++ = '-';
        *record++ = SYSLOG_DELIMITER;
        *record++ = '-';
    }
    *record++ = SYSLOG_DELIMITER;
     
    *record++ = ']'; /* End of the reocrd */

    log_info->pkt_length += record - temp;
}

void cnat_syslog_insert_record(
    cnat_syslog_logging_info_t *log_info,
    cnat_main_db_entry_t *db, dslite_table_entry_t *dslite_entry,
    cnat_session_entry_t *sdb, int bulk_alloc, syslog_event_type_t e_type)
{
    /* This record should like this - 
     * [EventName <L4> <Original Source IP> <Inside VRF Name> 
     *   <Original Source IPv6> < Translated Source IP> <Original Port> 
     *   <Translated First Source Port> <Translated Last Source Port>
     *   <Destination ip address> <destination port>]
     */
    u32 original_source = db->in2out_key.k.ipv4;
    u32 translated_ip = db->out2in_key.k.ipv4;
    cnat_user_db_entry_t  *udb = cnat_user_db + db->user_index;
    unsigned char *temp, *record;  
    u32 network_order_ipv6[4];

    temp = record =  &(log_info->current_logging_context->packet_data[
            CNAT_NFV9_IP_HDR_OFFSET + log_info->pkt_length]);

    if (PREDICT_FALSE(!udb)) {
        SYSLOG_DEBUG_PRINTF1("\nnull udb!");
        return;
    }

    /* Now we point to the location where record needs to be inserted */
    *record++ = '['; /* Open the record */

    /* Copy the record type */
    clib_memcpy(record, sys_log_event[e_type].event_name, 
        sys_log_event[e_type].name_length);
    record += sys_log_event[e_type].name_length;
    *record++ = SYSLOG_DELIMITER;
   
    /* Copy the Protocol type */
    if(PREDICT_FALSE(
        e_type == sessionbased_assign || e_type == sessionbased_withdraw ||
        e_type == sessionbased_assignD || e_type == sessionbased_withdrawD)) {
        u16 my_proto_mask;
        my_proto_mask = db->in2out_key.k.vrf & CNAT_PRO_MASK;
        if(PREDICT_TRUE(my_proto_mask == CNAT_TCP)) {
            *record++ = '6';
        } else if(PREDICT_TRUE(my_proto_mask == CNAT_UDP)) {
            *record++ = '1'; 
            *record++ = '7'; 
        } else {
            *record++ = '1'; 
        }
    } else {
        *record++ = SYSLOG_FIELD_ABSENT;        
    }

    *record++ = SYSLOG_DELIMITER;

     /* Copy the Original Source IP */
#ifdef DSLITE_USER_IPV4
     record += copy_ipv4_addr(record, original_source);
#else
    /*
     * Do not include inside ipv4 address for B4 element level port limiting
     */
     *record++ = SYSLOG_FIELD_ABSENT;        
#endif
     *record++ = SYSLOG_DELIMITER;
 
    /* copy configured VRF NAME */
    clib_memcpy(record, log_info->vrf_name, log_info->vrf_name_len);
    record += log_info->vrf_name_len;
    *record++ = SYSLOG_DELIMITER;
    
    /* Copy the IPV6 source address */
    /* CSCtt16960 Fix. */
    network_order_ipv6[0] = htonl(udb->ipv6[0]);
    network_order_ipv6[1] = htonl(udb->ipv6[1]);
    network_order_ipv6[2] = htonl(udb->ipv6[2]);
    network_order_ipv6[3] = htonl(udb->ipv6[3]);

    inet_ntop(AF_INET6,network_order_ipv6,record,INET6_ADDRSTRLEN);
    record += strlen(record);
    *record++ = SYSLOG_DELIMITER;

    /* Copy the translated IP address */
    record += copy_ipv4_addr(record, translated_ip);
    *record++ = SYSLOG_DELIMITER;
    
    /* Copy the Original port */
    if(e_type == sessionbased_assign || e_type == sessionbased_withdraw ||
       e_type == sessionbased_assignD || e_type == sessionbased_withdrawD) {
        record += u16_to_ascii_decimal_unaligned(
        record, db->in2out_key.k.port); 
    } else {
       *record++ = SYSLOG_FIELD_ABSENT; 
    }
    *record++ = SYSLOG_DELIMITER;

    /* Copy the start outside port */
    record += u16_to_ascii_decimal_unaligned(record, bulk_alloc);
    *record++ = SYSLOG_DELIMITER;

    /* Copy the last outside port */
    if(e_type == userbased_assign || e_type == userbased_withdraw) {
       record += u16_to_ascii_decimal_unaligned(record,
            (bulk_alloc + BULKSIZE_FROM_VRFMAP(dslite_entry) - 1));
    } else {
        *record++ = SYSLOG_FIELD_ABSENT;
    }
    *record++ = SYSLOG_DELIMITER;

    if(PREDICT_FALSE(e_type == sessionbased_assignD || e_type == sessionbased_withdrawD)) {
        if(sdb == NULL) {
            record += copy_ipv4_addr(record, db->dst_ipv4);
            *record++ = SYSLOG_DELIMITER;
            record += u16_to_ascii_decimal_unaligned(record, db->dst_port);
        } else {
            record += copy_ipv4_addr(record, sdb->v4_dest_key.k.ipv4);
            *record++ = SYSLOG_DELIMITER;
            record += u16_to_ascii_decimal_unaligned(record, sdb->v4_dest_key.k.port);
        }
    } else {
        *record++ = '-';
        *record++ = SYSLOG_DELIMITER;
        *record++ = '-';
    }
    *record++ = SYSLOG_DELIMITER;

    *record++ = ']'; /* End of the reocrd */

    log_info->pkt_length += record - temp;
}

#define SYSLOG_PRECHECK(entry, s_type)  \
    if(PREDICT_FALSE((entry)->syslog_logging_index == EMPTY)) { \
        SYSLOG_DEBUG_PRINTF1("\n1. Log Mapping failed") \
	    return; \
    } \
    logging_info = \
        cnat_syslog_logging_info_pool + (entry)->syslog_logging_index; \
    if(PREDICT_FALSE(logging_info->current_logging_context == NULL)) { \
        cnat_syslog_create_logging_context(logging_info, s_type); \
        if(PREDICT_FALSE(logging_info->current_logging_context == NULL)) { \
            SYSLOG_DEBUG_PRINTF1("\n2. Log Mapping failed") \
            return; \
        } \
    } 

void cnat_syslog_nat44_mapping_create(cnat_main_db_entry_t *db,
        cnat_vrfmap_t *vrfmap, cnat_session_entry_t * sdb
#ifndef NO_BULK_LOGGING
                       , int bulk_alloc
#endif
                       )
{
    cnat_syslog_logging_info_t *logging_info = 0; 
    syslog_event_type_t e_type;
    int start_port;

    SYSLOG_CONFIG_DEBUG_PRINTF(4,"In Function %s\n", __func__);
    SYSLOG_PRECHECK(vrfmap, NAT44)

#ifndef NO_BULK_LOGGING
    if(bulk_alloc > 0) { /* new bulk alloc - use bulk add template */
        e_type = userbased_assign;
        start_port = bulk_alloc;
    } else if(bulk_alloc == CACHE_ALLOC_NO_LOG_REQUIRED) {
        return; /* No logging required.. bulk port usage */
    }
    else { /* Individual logging .. fall back to old method */
#endif
    if(vrfmap->syslog_logging_policy == SESSION_LOG_ENABLE) {
        e_type = sessionbased_assignD;
    } else {
        e_type = sessionbased_assign;
    }
    start_port = db->out2in_key.k.port;
#ifndef NO_BULK_LOGGING
    }
#endif

    cnat_syslog_insert_nat44_record(logging_info, db, vrfmap, sdb, 
            start_port, e_type);

    /*
     * If we have exceeded the packet length, let us send the
     * packet now.  There is buffer of additional bytes beyond
     * max_pkt_length to ensure that the last add/delete record
     * can be stored safely.
     */

    if (PREDICT_FALSE(logging_info->pkt_length >
        logging_info->max_length_minus_max_record_size)) {
        cnat_syslog_send_pkt(logging_info);
    }
}

void cnat_syslog_ds_lite_mapping_create(cnat_main_db_entry_t *db,
        dslite_table_entry_t *dslite_entry, cnat_session_entry_t *sdb
#ifndef NO_BULK_LOGGING
                       , int bulk_alloc
#endif
                       )
{
    cnat_syslog_logging_info_t *logging_info = 0; 
    syslog_event_type_t e_type;
    int start_port;

    SYSLOG_PRECHECK(dslite_entry, DSLite)

#ifndef NO_BULK_LOGGING
    if(bulk_alloc > 0) { /* new bulk alloc - use bulk add template */
        e_type = userbased_assign;
        start_port = bulk_alloc;
    } else if(bulk_alloc == CACHE_ALLOC_NO_LOG_REQUIRED) {
        return; /* No logging required.. bulk port usage */
    }
    else { /* Individual logging .. fall back to old method */
#endif
    if(PREDICT_FALSE(dslite_entry->syslog_logging_policy == SESSION_LOG_ENABLE)) {
        e_type = sessionbased_assignD;
    } else {
        e_type = sessionbased_assign;
    }
    start_port = db->out2in_key.k.port;  
#ifndef NO_BULK_LOGGING
    }
#endif

    cnat_syslog_insert_record(logging_info, db, dslite_entry, sdb, 
            start_port, e_type);

    /*
     * If we have exceeded the packet length, let us send the
     * packet now.  There is buffer of additional bytes beyond
     * max_pkt_length to ensure that the last add/delete record
     * can be stored safely.
     */

    if (PREDICT_FALSE(logging_info->pkt_length >
        logging_info->max_length_minus_max_record_size)) {
        cnat_syslog_send_pkt(logging_info);
    }
}

void cnat_syslog_nat44_mapping_delete(cnat_main_db_entry_t *db,
        cnat_vrfmap_t *vrfmap, cnat_session_entry_t *sdb
#ifndef NO_BULK_LOGGING
        , int bulk_alloc
#endif
        )
{
    cnat_syslog_logging_info_t *logging_info = 0; 
    syslog_event_type_t e_type;
    int start_port;

    SYSLOG_CONFIG_DEBUG_PRINTF(4,"In Function %s\n", __func__);
    SYSLOG_PRECHECK(vrfmap, NAT44)

#ifndef NO_BULK_LOGGING
    if(bulk_alloc > 0) { /* new bulk alloc - use bulk add template */
        e_type = userbased_withdraw;
        start_port = bulk_alloc;
    } else if(bulk_alloc == CACHE_ALLOC_NO_LOG_REQUIRED) {
        return; /* No logging required.. bulk port usage */
    }
    else { /* Individual logging .. fall back to old method */
#endif
    if(vrfmap->syslog_logging_policy == SESSION_LOG_ENABLE) {
        e_type = sessionbased_withdrawD;
    } else {
        e_type = sessionbased_withdraw;
    }
    start_port = db->out2in_key.k.port;  
#ifndef NO_BULK_LOGGING
    }
#endif
    cnat_syslog_insert_nat44_record(logging_info, db, vrfmap, sdb, 
            start_port, e_type);
    /*
     * If we have exceeded the packet length, let us send the
     * packet now.  There is buffer of additional bytes beyond
     * max_pkt_length to ensure that the last add/delete record
     * can be stored safely.
     */
    if (PREDICT_FALSE(logging_info->pkt_length >
        logging_info->max_length_minus_max_record_size)) {
        cnat_syslog_send_pkt(logging_info);
    }
}

void cnat_syslog_ds_lite_mapping_delete(cnat_main_db_entry_t *db,
        dslite_table_entry_t *dslite_entry, cnat_session_entry_t *sdb
#ifndef NO_BULK_LOGGING
        , int bulk_alloc
#endif
        )
{
    cnat_syslog_logging_info_t *logging_info = 0; 
    syslog_event_type_t e_type;
    int start_port;

    SYSLOG_PRECHECK(dslite_entry, DSLite)

#ifndef NO_BULK_LOGGING
    if(bulk_alloc > 0) { /* new bulk alloc - use bulk add template */
        e_type = userbased_withdraw;
        start_port = bulk_alloc;
    } else if(bulk_alloc == CACHE_ALLOC_NO_LOG_REQUIRED) {
        return; /* No logging required.. bulk port usage */
    }
    else { /* Individual logging .. fall back to old method */
#endif
    if(PREDICT_FALSE(dslite_entry->syslog_logging_policy == SESSION_LOG_ENABLE)) {
        e_type = sessionbased_withdrawD;
    } else {
        e_type = sessionbased_withdraw;
    }
    start_port = db->out2in_key.k.port;  
#ifndef NO_BULK_LOGGING
    }
#endif
    cnat_syslog_insert_record(logging_info, db, dslite_entry, sdb, 
        start_port, e_type);

    /*
     * If we have exceeded the packet length, let us send the
     * packet now.  There is buffer of additional bytes beyond
     * max_pkt_length to ensure that the last add/delete record
     * can be stored safely.
     */

    if (PREDICT_FALSE(logging_info->pkt_length >
        logging_info->max_length_minus_max_record_size)) {
        cnat_syslog_send_pkt(logging_info);
    }
}

void cnat_syslog_dslite_insert_port_exceeded(
     cnat_syslog_logging_info_t *log_info,
     dslite_key_t   * key)
{
    /* This record should like this - 
     * [Portblockrunout <L4> <Original Source IP> <Inside VRF Name> 
     *   <Original Source IPv6> - <Original Port> - - - -]
     */
    u32 network_order_ipv6[4];
    unsigned char *temp, *record;  

    temp = record =  &(log_info->current_logging_context->packet_data[
            CNAT_NFV9_IP_HDR_OFFSET + log_info->pkt_length]);

    /* Now we point to the location where record needs to be inserted */
    *record++ = '['; /* Open the record */

    /* Copy the record type */
    clib_memcpy(record, sys_log_event[port_block_runout].event_name, 
        sys_log_event[port_block_runout].name_length);
    record += sys_log_event[port_block_runout].name_length;
    *record++ = SYSLOG_DELIMITER;
   
    u16 my_proto_mask;
    my_proto_mask = key->ipv4_key.k.vrf & CNAT_PRO_MASK;
    if(PREDICT_TRUE(my_proto_mask == CNAT_TCP)) {
        *record++ = '6';
    } else if(PREDICT_TRUE(my_proto_mask == CNAT_UDP)) {
        *record++ = '1'; 
        *record++ = '7'; 
    } else {
        *record++ = '1'; 
    }
    *record++ = SYSLOG_DELIMITER;

    /* Copy the Original Source IP */
    record += copy_ipv4_addr(record, key->ipv4_key.k.ipv4);
    *record++ = SYSLOG_DELIMITER;

    /* copy configured VRF NAME */
    clib_memcpy(record, log_info->vrf_name, log_info->vrf_name_len);
    record += log_info->vrf_name_len;
    *record++ = SYSLOG_DELIMITER;
    
    /* Copy the IPV6 source address */
    network_order_ipv6[0] = htonl(key->ipv6[0]);
    network_order_ipv6[1] = htonl(key->ipv6[1]);
    network_order_ipv6[2] = htonl(key->ipv6[2]);
    network_order_ipv6[3] = htonl(key->ipv6[3]);

    inet_ntop(AF_INET6,network_order_ipv6,record,INET6_ADDRSTRLEN);
    record += strlen(record);
    *record++ = SYSLOG_DELIMITER;

    *record++ = SYSLOG_FIELD_ABSENT; /* No translated source ip */
    *record++ = SYSLOG_DELIMITER;
    
    record += u16_to_ascii_decimal_unaligned(
        record, key->ipv4_key.k.port); 
    *record++ = SYSLOG_DELIMITER;
   
    *record++ = SYSLOG_FIELD_ABSENT; /* No translated start port */
    *record++ = SYSLOG_DELIMITER;

    *record++ = SYSLOG_FIELD_ABSENT; /* No translated end port */
    *record++ = SYSLOG_DELIMITER;

    /*No Destination Info*/
    *record++ = '-';
    *record++ = SYSLOG_DELIMITER;
    *record++ = '-';
    *record++ = SYSLOG_DELIMITER;

    *record++ = ']'; /* End of the reocrd */

    log_info->pkt_length += record - temp;
}

void cnat_syslog_ds_lite_port_limit_exceeded(
        dslite_key_t   * key,
        dslite_table_entry_t *dslite_entry)
{
    cnat_syslog_logging_info_t *logging_info = 0; 

    SYSLOG_PRECHECK(dslite_entry, DSLite)

    cnat_syslog_dslite_insert_port_exceeded(logging_info, key);

    /*
     * If we have exceeded the packet length, let us send the
     * packet now.  There is buffer of additional bytes beyond
     * max_pkt_length to ensure that the last add/delete record
     * can be stored safely.
     */

    if (PREDICT_FALSE(logging_info->pkt_length >
        logging_info->max_length_minus_max_record_size)) {
        cnat_syslog_send_pkt(logging_info);
    }
}

void cnat_syslog_nat44_insert_port_exceeded(
     cnat_syslog_logging_info_t *log_info,
     cnat_key_t   * key)
{
    /* This record should like this - 
     * [Portblockrunout <L4> <Original Source IP> <Inside VRF Name> 
     *  - - <Original Port> - - - -]
     */
    unsigned char *temp, *record;

    temp = record =  &(log_info->current_logging_context->packet_data[
            CNAT_NFV9_IP_HDR_OFFSET + log_info->pkt_length]);

    /* Now we point to the location where record needs to be inserted */
    *record++ = '['; /* Open the record */

    /* Copy the record type */
    clib_memcpy(record, sys_log_event[port_block_runout].event_name,
        sys_log_event[port_block_runout].name_length);
    record += sys_log_event[port_block_runout].name_length;
    *record++ = SYSLOG_DELIMITER;

    u16 my_proto_mask;
    my_proto_mask = key->k.vrf & CNAT_PRO_MASK;
    if(PREDICT_TRUE(my_proto_mask == CNAT_TCP)) {
        *record++ = '6';
    } else if(PREDICT_TRUE(my_proto_mask == CNAT_UDP)) {
        *record++ = '1';
        *record++ = '7';
    } else {
        *record++ = '1';
    }
    *record++ = SYSLOG_DELIMITER;

    /* Copy the Original Source IP */
    record += copy_ipv4_addr(record, key->k.ipv4);
    *record++ = SYSLOG_DELIMITER;

    /* copy configured VRF NAME */
    clib_memcpy(record, log_info->vrf_name, log_info->vrf_name_len);
    record += log_info->vrf_name_len;
    *record++ = SYSLOG_DELIMITER;

    /* No IPV6 source address for nat44 */
    *record++ = '-';
    *record++ = SYSLOG_DELIMITER;

    *record++ = '-'; /* No translated source ip */
    *record++ = SYSLOG_DELIMITER;

    record += u16_to_ascii_decimal_unaligned(
        record, key->k.port);
    *record++ = SYSLOG_DELIMITER;

    *record++ = '-'; /* No translated start port */
    *record++ = SYSLOG_DELIMITER;

    *record++ = '-'; /* No translated end port */
    *record++ = SYSLOG_DELIMITER;

    /*No Destination Info*/
    *record++ = '-';
    *record++ = SYSLOG_DELIMITER;
    *record++ = '-';
    *record++ = SYSLOG_DELIMITER;

    *record++ = ']'; /* End of the reocrd */

    log_info->pkt_length += record - temp;
}

void cnat_syslog_nat44_port_limit_exceeded(
        cnat_key_t   * key,
        cnat_vrfmap_t *vrfmap)
{   
    cnat_syslog_logging_info_t *logging_info = 0;
    
    SYSLOG_PRECHECK(vrfmap, NAT44)

    cnat_syslog_nat44_insert_port_exceeded(logging_info, key);
    
    /*
     * If we have exceeded the packet length, let us send the
     * packet now.  There is buffer of additional bytes beyond
     * max_pkt_length to ensure that the last add/delete record
     * can be stored safely.
     */

    if (PREDICT_FALSE(logging_info->pkt_length >
        logging_info->max_length_minus_max_record_size)) {
        cnat_syslog_send_pkt(logging_info);
    }
}

void cnat_syslog_nat44_insert_tcp_seq_mismatch(
     cnat_syslog_logging_info_t *log_info,
     cnat_main_db_entry_t *db)
{
    /* This record should like this - 
     * [TCPseqmismatch <L4> <Original Source IP> <Inside VRF Name> 
     *  - <Translated Source IP> <Original Port> <Translated Source Port> - - -]
     */
    unsigned char *temp, *record;

    temp = record =  &(log_info->current_logging_context->packet_data[
            CNAT_NFV9_IP_HDR_OFFSET + log_info->pkt_length]);

    /* Now we point to the location where record needs to be inserted */
    *record++ = '['; /* Open the record */

    /* Copy the record type */
    clib_memcpy(record, sys_log_event[tcp_seq_mismatch].event_name,
        sys_log_event[tcp_seq_mismatch].name_length);
    record += sys_log_event[tcp_seq_mismatch].name_length;
    *record++ = SYSLOG_DELIMITER;

    /*  Next field is TCP */
    *record++ = '6';
    *record++ = SYSLOG_DELIMITER;

    /* Copy the Original Source IP */
    record += copy_ipv4_addr(record, db->in2out_key.k.ipv4);
    *record++ = SYSLOG_DELIMITER;

    /* copy configured VRF NAME */
    clib_memcpy(record, log_info->vrf_name, log_info->vrf_name_len);
    record += log_info->vrf_name_len;
    *record++ = SYSLOG_DELIMITER;

    /* No IPV6 source address for nat44 */
    *record++ = '-';
    *record++ = SYSLOG_DELIMITER;

    record += copy_ipv4_addr(record, db->out2in_key.k.ipv4);
    *record++ = SYSLOG_DELIMITER;

    record += u16_to_ascii_decimal_unaligned(
        record,  db->in2out_key.k.port);
    *record++ = SYSLOG_DELIMITER;

    record += u16_to_ascii_decimal_unaligned(
        record,  db->out2in_key.k.port);
    *record++ = SYSLOG_DELIMITER;

    *record++ = '-'; /* No translated end port */
    *record++ = SYSLOG_DELIMITER;

    /*No Destination Info*/
    *record++ = '-';
    *record++ = SYSLOG_DELIMITER;
    *record++ = '-';
    *record++ = SYSLOG_DELIMITER;

    *record++ = ']'; /* End of the reocrd */

    log_info->pkt_length += record - temp;
}

void cnat_syslog_nat44_tcp_seq_mismatch(
        cnat_main_db_entry_t *db,
        cnat_vrfmap_t *vrfmap)
{   
    cnat_syslog_logging_info_t *logging_info = 0;
    
    SYSLOG_PRECHECK(vrfmap, NAT44)

    cnat_syslog_nat44_insert_tcp_seq_mismatch(logging_info, db);
    
    /*
     * If we have exceeded the packet length, let us send the
     * packet now.  There is buffer of additional bytes beyond
     * max_pkt_length to ensure that the last add/delete record
     * can be stored safely.
     */

    if (PREDICT_FALSE(logging_info->pkt_length >
        logging_info->max_length_minus_max_record_size)) {
        cnat_syslog_send_pkt(logging_info);
    }
}
#endif
