/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@eurecom.fr

  Address      : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.

 *******************************************************************************/
/*! \file sgi.h
* \brief
* \author Lionel Gauthier
* \company Eurecom
* \email: lionel.gauthier@eurecom.fr
*/
#ifndef SGI_H_
#define SGI_H_

#include <net/ethernet.h>
#include <netinet/in.h>
#include <pthread.h>
 #include <stdio.h>

#ifdef ENABLE_USE_PCAP_FOR_SGI
#include <pcap.h>
#endif

//#define SGI_TEST
// Packets include a ethernet header if SGI_PF_PACKET is defined
#define SGI_PF_PACKET

#include "common_types.h"
#include "hashtable.h"
#include "obj_hashtable.h"
#include "sgw_lite_ie_defs.h"
#include "ip_forward_messages_types.h"

//# define SGI_IF_DEBUG(x, args...)
//# define SGI_IF_ERROR(x, args...)
//# define SGI_IF_WARNING(x, args...)
//-----------------------------------------------------------------------------
#ifndef SGI_IF_DEBUG
# define SGI_IF_DEBUG(x, args...) do { fprintf(stdout, "[SGI_IF][D]"x, ##args); } \
    while(0)
#endif
#ifndef SGI_IF_ERROR
# define SGI_IF_ERROR(x, args...) do { fprintf(stderr, "[SGI_IF][E]"x, ##args); } \
    while(0)
#endif
#ifndef SGI_IF_WARNING
 # define SGI_IF_WARNING(x, args...) do { fprintf(stderr, "[SGI_IF][W]"x, ##args); } \
     while(0)
#endif

// refer to 3GPP TS 23.203 V10.6.0 (2012-03) page 33
//#define MAX_DEFINED_QCI 0
#define MAX_DEFINED_QCI                    9
#define SGI_MIN_EPS_BEARER_ID              5
#define SGI_MAX_SERVED_USERS_PER_PGW      1024
#define SGI_MAX_EPS_BEARERS_PER_USER      11
#define MAX_DEFINED_IPV6_ADDRESSES_PER_UE 16


#define NIP6ADDR(addr) \
        ntohs((addr)->s6_addr16[0]), \
        ntohs((addr)->s6_addr16[1]), \
        ntohs((addr)->s6_addr16[2]), \
        ntohs((addr)->s6_addr16[3]), \
        ntohs((addr)->s6_addr16[4]), \
        ntohs((addr)->s6_addr16[5]), \
        ntohs((addr)->s6_addr16[6]), \
        ntohs((addr)->s6_addr16[7])

#define NIPADDR(addr) \
        (uint8_t)(addr & 0x000000FF), \
        (uint8_t)((addr & 0x0000FF00) >> 8), \
        (uint8_t)((addr & 0x00FF0000) >> 16), \
        (uint8_t)((addr & 0xFF000000) >> 24)

#define NMACADDR(addr) \
        (uint8_t)(addr[0]), \
        (uint8_t)(addr[1]), \
        (uint8_t)(addr[2]), \
        (uint8_t)(addr[3]), \
        (uint8_t)(addr[4]), \
        (uint8_t)(addr[5])
#ifdef VLAN8021Q
typedef struct vlan_tag_s {
    uint16_t       vlan_tpid;              /* ETH_P_8021Q */
    uint16_t       vlan_tci;               /* VLAN TCI */
} vlan_tag_t __attribute__ ((__packed__));

typedef struct ether_header_8021q_s
{
  u_int8_t  ether_dhost[ETH_ALEN];	/* destination eth addr	*/
  u_int8_t  ether_shost[ETH_ALEN];	/* source ether addr	*/
  vlan_tag_t ether_vlan8021q;
  u_int16_t ether_type;		        /* packet type ID field	*/
} ether_header_8021q_t __attribute__ ((__packed__));
#endif

typedef struct arphdr_s {
	__be16		ar_hrd;		/* format of hardware address	*/
	__be16		ar_pro;		/* format of protocol address	*/
	unsigned char	ar_hln;		/* length of hardware address	*/
	unsigned char	ar_pln;		/* length of protocol address	*/
	__be16		ar_op;		/* ARP opcode (command)		*/
	 /*
	  *	 Ethernet looks like this : This bit is variable sized however...
	  */
	unsigned char		ar_sha[ETH_ALEN];	/* sender hardware address	*/
	unsigned char		ar_sip[4];		/* sender IP address		*/
	unsigned char		ar_tha[ETH_ALEN];	/* target hardware address	*/
	unsigned char		ar_tip[4];		/* target IP address		*/

}arphdr_t;

typedef struct sgi_teid_mapping_s {
	int                        is_outgoing_ipv4_packet_seen;
	int                        is_outgoing_ipv6_packet_seen;
	Teid_t                     enb_S1U_teid;        ///< dest tunnel identifier
	qci_t                      qci;
	ebi_t                      eps_bearer_id;       ///< EPS bearer identifier
    char                       hw_addrlen;
#ifdef SGI_UE_ETHER_TRAFFIC
    char                       ue_mac_addr[8];
#endif
    struct in6_addr            in6_addr_captured[MAX_DEFINED_IPV6_ADDRESSES_PER_UE];
    struct in_addr             in_add_captured;
} sgi_teid_mapping_t;


// Main goal is to do an association IP address <-> MAC address
// may be usefull for netfilter that does not capture MAC address
// if not set on certain hook points (table,path).
typedef struct sgi_addr_mapping_s {
	int                        is_outgoing_packet_seen;
	ebi_t                      eps_bearer_id;       ///< EPS default bearer identifier, (really ?)
	Teid_t                     enb_S1U_teid;        ///< dest tunnel identifier, for default bearer
	Teid_t                     sgw_S1U_teid;        ///< dest tunnel identifier, for default bearer
#ifdef SGI_UE_ETHER_TRAFFIC
    char                       ue_mac_addr[ETH_ALEN];
#endif
} sgi_addr_mapping_t;




typedef struct sgi_data_s {
    int                 sd[SGI_MAX_EPS_BEARERS_PER_USER];
    int                 sd6;
    char               *interface_name;
    int                 interface_name_len;
    int                 interface_index;
#ifndef SGI_UE_ETHER_TRAFFIC
    char                interface_hw_address[ETH_ALEN];
#ifdef VLAN8021Q
    struct ether_header_8021q_s eh;
#else
    struct ether_header eh;
#endif
#endif
    uint32_t            ipv4_addr;
    hash_table_t       *teid_mapping;
    hash_table_t       *addr_v4_mapping;
    obj_hash_table_t   *addr_v6_mapping;
    pthread_t           capture_on_sgi_if_thread;
    pthread_mutex_t     thread_started_mutex;
    int                 thread_started;

    void               *sock_mmap_ring;
    struct iovec       *malloc_ring;

    int                 hw_address_of_router_captured;
    uint32_t            local_addr_v4_4_hw_address_router_capture;
    uint32_t            ipv4_addr_of_router;
    char                hss_associated;


#ifdef ENABLE_USE_PCAP_FOR_SGI
    //brief PCAP descriptor for capturing packets on SGI interface.
    pcap_t * pcap_descr;
#endif
#define SGI_BUFFER_RECV_LEN 3000
    unsigned char recv_buffer[sizeof(struct ether_header) + SGI_BUFFER_RECV_LEN][SGI_MAX_EPS_BEARERS_PER_USER];

} sgi_data_t;


typedef struct sgi_read_thread_args_s {
    sgi_data_t     *sgi_data;
    int            socket_index;
}sgi_read_thread_args_t;
//-----------------------------------------------------------------------------

#include "mme_config.h"


#define SGI_MAX_EPS_BEARERS (SGI_MAX_SERVED_USERS_PER_PGW*SGI_MAX_EPS_BEARERS_PER_USER)



//-----------------------------------------------------------------------------
// sgi_task.c
//-----------------------------------------------------------------------------
#include "spgw_config.h"
int sgi_init(const pgw_config_t *pgw_config_p);
char* sgi_status_2_str(SGIStatus_t statusP);

//-----------------------------------------------------------------------------
// sgi_nf.c
//-----------------------------------------------------------------------------
#ifdef ENABLE_USE_NETFILTER_FOR_SGI
void* sgi_nf_fw_2_gtpv1u_thread(void *args_p);
#endif

//-----------------------------------------------------------------------------
// sgi_pcap.c
//-----------------------------------------------------------------------------
#ifdef ENABLE_USE_PCAP_FOR_SGI
#    define SGI_PCAPMAXBYTES2CAPTURE 65535
#    define SGI_PCAPTIMEDELAYKERNEL2USERSPACE 1000

/*! \fn void* sgi_pcap_loop(void*)
* \brief
* \param[in]  devname The name of the device (ex "eth1") that will be listened for capturing packets.
* \param[in]  iif     The interface identifier that will be listened for capturing packets.
*/
void* sgi_pcap_fw_2_gtpv1u_thread(void*);
#endif
//-----------------------------------------------------------------------------
// sgi_util.c
//-----------------------------------------------------------------------------
void  sgi_print_hex_octets(unsigned char* dataP, unsigned long sizeP);
char* sgi_status_2_str    (SGIStatus_t statusP);
char* sgi_arpopcode_2_str (unsigned short int opcodeP);
unsigned short in_cksum(unsigned short *addr, int len);
void  sgi_send_arp_request(sgi_data_t *sgi_dataP, char* dst_ip_addrP);

//-----------------------------------------------------------------------------
// sgi_socket.c
//-----------------------------------------------------------------------------
int sgi_create_sockets(sgi_data_t *sgi_data_p);
int sgi_send_data(uint8_t *buffer, uint32_t length, sgi_data_t *sgi_dataP, Teid_t originating_sgw_S1u_teidP);
#ifdef SGI_TEST
unsigned short in_cksum(unsigned short *addr, int len);
void sgi_test_send_ping(sgi_data_t *sgi_dataP, uint32_t markP, uint64_t src_mac_addrP, uint64_t dst_mac_addrP, char* src_ip_addrP, char* dst_ip_addrP);
#endif
#ifdef ENABLE_USE_RAW_SOCKET_FOR_SGI
void  sgi_sock_raw_cleanup_handler(void *args_p);
void* sgi_sock_raw_fw_2_gtpv1u_thread(void* args_p);
#endif

//-----------------------------------------------------------------------------
// sgi_egress.c
//-----------------------------------------------------------------------------
void sgi_forward_ip_packet(sgi_data_t *sgi_dataP, struct ether_header *ehP, int packet_sizeP, sgi_addr_mapping_t *addr_mappingP);
#ifdef ENABLE_USE_RAW_SOCKET_FOR_SGI
void sgi_process_raw_packet(sgi_data_t *sgi_dataP, unsigned char* dataP, int packet_sizeP);
#endif
#endif /* SGI_H_ */