/*
 *------------------------------------------------------------------
 * nat64_db.h - Stateful NAT64 translation database definitions
 *
 * Copyright (c) 2010-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.
 *------------------------------------------------------------------
 */
#ifndef __NAT64_DB_H__
#define __NAT64_DB_H__

#include "cnat_cli.h"
#include "index_list.h"
#include "cnat_ports.h"
#include "cnat_db.h"
#include "nat64_defs.h"
#include "cnat_bulk_port_defs.h"

nat64_vrfmap_t *nat64_map_by_vrf;

#define SESSION_OPT

#define HASH_ENHANCE 4


#define NAT64_MAIN_DB_SIZE \
               (PLATFORM_NAT64_MAX_SESSIONS / PLATFORM_CNAT_INSTS)
#define NAT64_MAIN_HASH_SIZE \
         (HASH_ENHANCE * PLATFORM_CNAT_MAIN_PRELIM_HASH_SIZE) 

#define NAT64_MAIN_HASH_MASK (NAT64_MAIN_HASH_SIZE-1)


/* nb: 200000 users / 64 CNAT = 3125, 76% occupancy */
#define NAT64_USER_HASH_SIZE CNAT_USER_HASH_SIZE
#define NAT64_USER_HASH_MASK (NAT64_USER_HASH_SIZE-1)

/* Number of sessions per BIB entry/NAT64 translation
   - nsessions is u16 type. So selected 0xFFFF
   - Ideally Sessions per transltion will not reach the limit
   - Only DoS can possible. It can take care of it   */
#define NAT64_MAX_SESSIONS_PER_BIB  0xFFFF

/* No. of per ip/port config will be limited to 1000 */
/* totally 25K across all instances) */
#define NAT64_TIMEOUT_HASH_SIZE \
                       PLATFORM_NAT64_TIMEOUT_HASH_SIZE 

#define NAT64_TIMEOUT_HASH_MASK (NAT64_TIMEOUT_HASH_SIZE - 1)
#define NAT64_TIMEOUT_FULL_MASK 0xFFFFFFFFFFFFFFFF


#define FORCE_DEL 1 /* Delete static BIB entries as well */

/* default timeout values */
#define NAT64_UDP_DEFAULT          300  /* 5 min */
#define NAT64_UDP_MIN              120  /* 2 min */
#define NAT64_TCP_TRANS            240  /* 4 min */
#define NAT64_TCP_EST             7200  /* 2 hrs */
#define NAT64_TCP_V4_SYN             6  /* 6 sec */
#define NAT64_FRAG_MIN               2  /* 2 sec */
#define NAT64_ICMP_DEFAULT          60  /* 1 min */


#define NAT64_V6_GET_HASH(in_key, hash, mask) \
    a = in_key->ipv6[0] ^ in_key->ipv6[1] ^ in_key->ipv6[2] ^ in_key->ipv6[3] \
         ^ ((in_key->port << 16) | in_key->vrf); \
    b = c = 0x9e3779b9;\
    /* Jenkins hash, arbitrarily use c as the "answer" */ \
    hash_mix32(a, b, c); \
    hash = c & mask; \


#define NAT64_V4_GET_HASH(in_key, hash, mask) \
    a = in_key.ipv4 ^ ((in_key.port << 16) | in_key.vrf); \
    b = c = 0x9e3779b9; \
    /* Jenkins hash, arbitrarily use c as the "answer" */ \
    hash_mix32(a, b, c); \
    hash = c & mask;



#define NAT64_V6_GET_SESSION_HASH(bib_index, in_addr, port, vrf, hash, mask) \
    a = bib_index ^ in_addr[0] ^ in_addr[1] ^ in_addr[2] ^ in_addr[3] \
            ^ port ^ vrf; \
    b = c = 0x9e3779b9; \
    /* Jenkins hash, arbitrarily use c as the "answer" */ \
    hash_mix32(a, b, c); \
    hash = c & mask;

#define NAT64_V4_GET_SESSION_HASH(bib_index, in_addr, port, vrf, hash, mask) \
    a = bib_index ^ in_addr ^ port ^ vrf; \
    b = c = 0x9e3779b9; \
    /* Jenkins hash, arbitrarily use c as the "answer" */ \
    hash_mix32(a, b, c); \
    hash = c & mask;


extern index_slist_t        *nat64_bib_out2in_hash;
extern index_slist_t        *nat64_bib_in2out_hash;
extern index_slist_t        *nat64_bib_user_hash;
extern index_slist_t        *nat64_session_out2in_hash;
#ifndef SESSION_OPT
extern index_slist_t        *nat64_session_in2out_hash;
#endif
extern index_slist_t        *nat64_frag_out2in_hash;
extern index_slist_t        *nat64_frag_in2out_hash;
extern index_slist_t        *nat64_timeout_hash;


/*
 * nat64_ bib_entry_t
 * This structure depicts Binding Information Base of NAT64 sessions. 
 * It stores information about the inside v6 source transport address and 
 * corresponding outside v4 source transport address for each protocol.
 */

typedef struct {

    index_slist_t     nat64_bib_out2in_hash;
    index_slist_t     nat64_bib_in2out_hash;

    /* 0x08 */
    u16               flags; /* flags in cnat_db.h (cnat_main_db_entry_t) */
#define NAT64_DB_FLAG_STATIC_PORT CNAT_DB_FLAG_STATIC_PORT
#define NAT64_DB_NAT64_FLAG       CNAT_DB_NAT64_FLAG
#define NAT64_DB_FLAG_ALG_ENTRY   CNAT_DB_FLAG_ALG_ENTRY
#define NAT64_DB_FLAG_PCPI        CNAT_DB_FLAG_PCPI 
#define NAT64_DB_FLAG_PCPE        CNAT_DB_FLAG_PCPE

    /* 0x0A */
    u16              nat64_inst_id;
    /* 0x0C */
    u32              user_index;

    /* 0x10 */
    nat64_v4_key_t     v4_out_key;

    /* 0x18 */
    nat64_v6_key_t     v6_in_key;      

    /* 0x2C */
    index_dlist_t    user_ports;   
    /* 0x34 */
    u32              session_head_index;
     /* 0x38 - 56B*/
    u16              nsessions;
    u16              pad2;

    /* 0x3C - 60B */
    u32              in2outpkts;
    u32              out2inpkts;
    /* 0x44 - 68B */

    /* 0x42 - 70B */
    union {                     /* used by FTP ALG, pkt len delta due to FTP PORT cmd */
    u16 delta;
    i8  alg_dlt[2];             /* two delta values, 0 for previous, 1 for current */
    u16 il;                     /* Used to indicate if interleaved mode is used
                                   in case of RTSP ALG */
    } alg;

    u16 temp1;

    u32 entry_expires;

    u32 temp3;
    /* unused, temp1 ,temp2 and temp3 put to make it in sync with nat44 main db entry size */
    /* size of = 0x54 = 84 B */
    u32 unused;

} nat64_bib_entry_t ;

/* 
 * nat64_bib_user_entry_t
 * This structure stores information about translations of a particular user 
 * (User here refers to a same inside source address)
 */
typedef struct {
    /* 0x00 */
    index_slist_t user_hash;    
     /* 0x04 */
    u16 ntranslations;          
    /* 0x06 */
    u8 icmp_msg_count;
    /* 0x07 */
    u8 flags;                
#define NAT64_USER_DB_NAT64_FLAG CNAT_USER_DB_NAT64_FLAG

    /* 0x08 */ 
    u32 translation_list_head_index;
    /* 0x0C */
    u32 portmap_index;          
    /* 0x10 */
    nat64_v6_key_t     v6_in_key;      
    /* 0x24 = 36 B */

    u32 align1; /* Make it 8B boundary  and in sync with nat44 user db entry size */
#ifndef NO_BULK_LOGGING
    /* size of = 0x28 = 40 B */
    /* Now adding 8 more bytes for bulk allocation.. This makes it
     * 0x30 (48).  For nat64 stful, we may support bulk allocation
     * later */
    /* Indicates the currently used bulk port range */
    i16 bulk_port_range_cache[BULK_RANGE_CACHE_SIZE];
#endif /*  NO_BULK_LOGGING */
} nat64_bib_user_entry_t;

/*
 * nat64_session_entry_t
 * This structure represents the session table. It maintains the information 
 * about the flow of the packets. It would consist of source and destination 
 * (inside and outside) ipv4 and ipv4 transport addresses. 
 */
typedef struct {

    /* 0x00 */
    index_slist_t   nat64_session_out2in_hash;

    /* 0x04 */ 
    u32 bib_index; /* would point to v4/v6 src transport address */

    /* 0x08 */
    nat64_v4_key_t     v4_dest_key;

#ifndef SESSION_OPT
   index_slist_t   nat64_session_in2out_hash;
   nat64_v6_key_t     v6_dest_key;
#endif

    /* 0x10 */
    u16  flags;/* Will be used for flags same as nat44 session */ 

    /* 0x12 */
    u16 timeout; 

    /* 0x14 */
    u32 entry_expires;
    /* 0x18 */
    index_dlist_t    bib_list;
    /* 0x20 = 32 B */

    union {                     /* alg same as cnat_main_db_t */
    u16 delta;
    i8  alg_dlt[2];
    u16 il;
    } alg;

    /* 0x22 */
    u16  tcp_flags; /* Mainly TCP events - check nat64_tcp_sm.h */

    /* 0x24  */
    u32 tcp_seq_num;

    /* 0x28 */      /* unused1, unused2 and unused3 are put to make it in sync with 
                     * cnat_session_db */
    u32 unused1;

    /* 0x2C */
    u32 unused2;

    /* 0x30 */
    u16 unused3;

    /* 0x32 - 50B */

} nat64_session_entry_t;

/*
 * nat64_session_tcp_init_entry_t 
 * This structure will be used to store information about v4 initiation 
 * tcp entries.
 */
typedef struct {
    nat64_v6_key_t     v6_in_key;      
    nat64_v4_key_t     v4_out_key;
} nat64_session_tcp_init_entry_t;

/* 
 * nat64_in_v6_frag_entry_t 
 * This structure will be used to store information about fragment flows 
 * that are coming from inside v6 hosts.
 */
typedef struct {
     index_slist_t  nat64_frag_in2out_hash;

     u32 v6_src_addr[4];
     u32 v6_destn_addr[4];
     u32 frag_iden;
     u16  vrf;
     u16  pad1;
} nat64_in_v6_frag_entry_t ;

/*
 * nat64_out_v4_frag_entry_t 
 * This structure will be used to store information about fragment flows 
 * that are coming from outside v4 machines.
 */
typedef struct {
     index_slist_t  nat64_frag_out2in_hash;

     u32 v4_src_addr;
     u32 v4_destn_addr;
     u16 frag_iden;
     u16  vrf;
} nat64_out_v4_frag_entry_t ;

/*
 * nat64_timeout _t 
 * These following structures will be used to store information destination 
 * timeouts configured.
 */
typedef struct {
    nat64_v4_key_t timeout_key;
    u16        timeout_value;
} nat64_timeout_t;

/*
 * nat64_timeout_db_entry_t 
 */
typedef struct {
    nat64_timeout_t  t_key;
    index_slist_t  t_hash;
} nat64_timeout_db_entry_t;


typedef union {
    cnat_main_db_entry_t nat44_main_db;
    nat64_bib_entry_t nat64_bib_db; 
} cgse_nat_db_entry_t;

typedef union {
    cnat_session_entry_t   nat44_session_db;
    nat64_session_entry_t     nat64_session_db;
} cgse_nat_session_db_entry_t;

typedef union {
    cnat_user_db_entry_t   nat44_user_db;
    nat64_bib_user_entry_t nat64_user_db;
} cgse_nat_user_db_entry_t;

extern index_slist_t        *nat64_bib_out2in_hash;
extern index_slist_t        *nat64_bib_in2out_hash;
extern index_slist_t        *nat64_bib_user_hash;
extern index_slist_t        *nat64_session_out2in_hash;
extern index_slist_t        *nat64_session_in2out_hash;
extern index_slist_t        *nat64_frag_out2in_hash;
extern index_slist_t        *nat64_frag_in2out_hash;
extern index_slist_t        *nat64_timeout_hash;

extern nat64_bib_entry_t               *nat64_bib_db;
extern nat64_bib_user_entry_t          *nat64_bib_user_db;
extern nat64_session_entry_t           *nat64_session_db;
extern nat64_in_v6_frag_entry_t        *nat64_in_frag_db;
extern nat64_out_v4_frag_entry_t       *nat64_out_frag_db;
extern nat64_session_tcp_init_entry_t  *nat64_tcp_init_db ;
extern nat64_timeout_db_entry_t        *nat64_timeout_db;

extern nat64_table_entry_t         nat64_table_array[NAT64_MAX_NAT64_ENTRIES];
extern nat64_table_entry_t         *nat64_table_ptr;

extern cgse_nat_db_entry_t         *cgse_nat_db;
extern cgse_nat_user_db_entry_t    *cgse_user_db;
extern cgse_nat_session_db_entry_t *cgse_session_db;

void nat64_bib_user_db_delete (nat64_bib_user_entry_t *up);

nat64_bib_user_entry_t*
nat64_bib_user_db_create_entry(nat64_v6_key_t *uki, u32 bucket,
                          u32 portmap_index);

nat64_bib_user_entry_t*
nat64_bib_user_db_lookup_entry(nat64_v6_key_t *uki, u32 *bucket);


nat64_bib_entry_t*
nat64_bib_db_lookup_entry(nat64_v6_key_t *ki);

void nat64_bib_db_in2out_hash_delete (nat64_bib_entry_t *ep);

void nat64_bib_db_out2in_hash_delete (nat64_bib_entry_t *ep);

nat64_bib_entry_t *
nat64_create_bib_db_entry_and_hash(nat64_v6_key_t *ki,
                                   nat64_v4_key_t *ko,
                                   nat64_bib_user_entry_t *udb);


void nat64_delete_bib_db_entry (nat64_bib_entry_t *ep, u8 force);

nat64_bib_entry_t *
nat64_bib_db_lookup_entry_out2in (nat64_v4_key_t *ko);

nat64_bib_entry_t *
nat64_get_bib_db_entry (nat64_v6_key_t *ki,
                        port_pair_t port_pair_type,
                        port_type_t port_type,
                        cnat_gen_icmp_info *info);


nat64_bib_entry_t*
nat64_create_static_bib_db_entry (nat64_v6_key_t *ki,
                                  nat64_v4_key_t *ko,
                                  nat64_table_entry_t *my_table,
                                  cnat_gen_icmp_info   *info);



//void nat64_session_db_in2out_hash_delete (nat64_session_entry_t *ep);
void nat64_session_db_out2in_hash_delete (nat64_session_entry_t *ep);

/*nat64_session_entry_t *
nat64_session_db_lookup_entry(nat64_v6_key_t *ki, u32 bib_index); */


nat64_session_entry_t *
nat64_session_db_lookup_entry_out2in (nat64_v4_key_t *ko,u32 bib_index);

/*
nat64_session_entry_t *
nat64_create_session_db_entry(nat64_v6_key_t *ki,
                              nat64_v4_key_t *ko,
                              nat64_bib_entry_t *bdb);
*/
nat64_session_entry_t *
nat64_create_session_db_entry_v2( nat64_v4_key_t *ko,
                              nat64_bib_entry_t *bdb);


//void nat64_delete_session_db_entry (nat64_session_entry_t *ep);
void nat64_delete_session_db_entry_v2 (nat64_session_entry_t *ep, u8 force);

u32 nat64_timeout_db_hash_lookup (nat64_v4_key_t t_key);

u16 query_and_update_db_timeout_nat64(nat64_session_entry_t *db);

void nat64_timeout_db_hash_add (nat64_timeout_db_entry_t *t_entry);

u16 nat64_timeout_db_create (nat64_timeout_t t_entry);

void nat64_timeout_db_delete(nat64_v4_key_t t_key);

#define NAT64_CMP_V6_KEY(key1, key2) \
       memcmp(key1, key2, sizeof(nat64_v6_key_t))

#define NAT64_CMP_V4_KEY(key1, key2) \
       memcmp(key1, key2, sizeof(nat64_v4_key_t))


#define NAT64_CMP_V6_IP(ip1, ip2) \
       memcmp(ip1, ip2, (sizeof(u32) * 4))


#define NAT64_CMP_V6_KEY1(key1, key2) \
    (key1.ipv6[0] == key2.ipv6[0]) && (key1.ipv6[1] == key2.ipv6[1]) && \
    (key1.ipv6[2] == key2.ipv6[2]) &&  (key1.ipv6[3] == key2.ipv6[3]) && \
    (key1.port == key2.port) && (key1.vrf == key2.vrf) 


#define NAT64_CMP_V6_IP1(ip1, ip2) \
    ((ip1[0] == ip2[0]) && (ip1[1] == ip2[1]) && \
    (ip1[2] == ip2[2]) && (ip1[3] == ip2[3]))

#define NAT64_CMP_V4_KEY1(key1, key2) \
       (key1.key64 == key2.key64)


extern u8  nat64_timeout_dirty_flag[NAT64_MAX_NAT64_ENTRIES];

#endif