/*******************************************************************************
    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_util.c
* \brief
* \author Lionel Gauthier
* \company Eurecom
* \email: lionel.gauthier@eurecom.fr
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "sgi.h"

#define FW_2_PRINT_BUFFER_LEN 10000
static char fw_2_print_buffer[FW_2_PRINT_BUFFER_LEN];
//-----------------------------------------------------------------------------
void sgi_print_hex_octets(unsigned char* dataP, unsigned long sizeP)
//-----------------------------------------------------------------------------
{
  unsigned long octet_index = 0;
  unsigned long buffer_marker = 0;
  unsigned char aindex;
  struct timeval tv;
  struct timezone tz;
  char timeofday[64];
  unsigned int h,m,s;

  if (dataP == NULL) {
    return;
  }

  gettimeofday(&tv, &tz);
  h = (tv.tv_sec/3600) % 24;
  m = (tv.tv_sec / 60) % 60;
  s = tv.tv_sec % 60;
  snprintf(timeofday, 64, "%02d:%02d:%02d.%06d", h,m,s,tv.tv_usec);

  SGI_IF_DEBUG("%s------+-------------------------------------------------+\n",timeofday);
  SGI_IF_DEBUG("%s      |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |\n",timeofday);
  SGI_IF_DEBUG("%s------+-------------------------------------------------+\n",timeofday);

  for (octet_index = 0; octet_index < sizeP; octet_index++) {
    if ((octet_index % 16) == 0) {
      if (octet_index != 0) {
        buffer_marker+=snprintf(&fw_2_print_buffer[buffer_marker], FW_2_PRINT_BUFFER_LEN - buffer_marker, " |\n");
        SGI_IF_DEBUG("%s%s",timeofday, fw_2_print_buffer);
        buffer_marker = 0;
      }

      buffer_marker+=snprintf(&fw_2_print_buffer[buffer_marker], FW_2_PRINT_BUFFER_LEN - buffer_marker, " %04ld |", octet_index);
    }

    /*
     * Print every single octet in hexadecimal form
     */
    buffer_marker+=snprintf(&fw_2_print_buffer[buffer_marker], FW_2_PRINT_BUFFER_LEN - buffer_marker, " %02x", dataP[octet_index]);
    /*
     * Align newline and pipes according to the octets in groups of 2
     */
  }

  /*
   * Append enough spaces and put final pipe
   */
  for (aindex = octet_index; aindex < 16; ++aindex)
    buffer_marker+=snprintf(&fw_2_print_buffer[buffer_marker], FW_2_PRINT_BUFFER_LEN - buffer_marker, "   ");

  //SGI_IF_DEBUG("   ");
  buffer_marker+=snprintf(&fw_2_print_buffer[buffer_marker], FW_2_PRINT_BUFFER_LEN - buffer_marker, " |\n");
  SGI_IF_DEBUG("%s%s",timeofday,fw_2_print_buffer);
}

//-----------------------------------------------------------------------------
char* sgi_status_2_str(SGIStatus_t statusP)
//-----------------------------------------------------------------------------
{
  switch (statusP) {
  case SGI_STATUS_OK:
    return "SGI_STATUS_OK";
    break;

  case SGI_STATUS_ERROR_CONTEXT_ALREADY_EXIST:
    return "SGI_STATUS_ERROR_CONTEXT_ALREADY_EXIST";
    break;

  case SGI_STATUS_ERROR_CONTEXT_NOT_FOUND:
    return "SGI_STATUS_ERROR_CONTEXT_NOT_FOUND";
    break;

  case SGI_STATUS_ERROR_INVALID_MESSAGE_FORMAT:
    return "SGI_STATUS_ERROR_INVALID_MESSAGE_FORMAT";
    break;

  case SGI_STATUS_ERROR_SERVICE_NOT_SUPPORTED:
    return "SGI_STATUS_ERROR_SERVICE_NOT_SUPPORTED";
    break;

  case SGI_STATUS_ERROR_SYSTEM_FAILURE:
    return "SGI_STATUS_ERROR_SYSTEM_FAILURE";
    break;

  case SGI_STATUS_ERROR_NO_RESOURCES_AVAILABLE:
    return "SGI_STATUS_ERROR_NO_RESOURCES_AVAILABLE";
    break;

  case SGI_STATUS_ERROR_NO_MEMORY_AVAILABLE:
    return "SGI_STATUS_ERROR_NO_MEMORY_AVAILABLE";
    break;

  default:
    return "UNKNOWN_SGI_STATUS";
  }
}

//-----------------------------------------------------------------------------
char* sgi_arpopcode_2_str(unsigned short int opcodeP)
//-----------------------------------------------------------------------------
{
  switch (opcodeP) {
  case ARPOP_REQUEST:
    return "ARPOP_REQUEST";
    break;

  case ARPOP_REPLY:
    return "ARPOP_REPLY";
    break;

  case ARPOP_RREQUEST:
    return "ARPOP_RREQUEST";
    break;

  case ARPOP_RREPLY:
    return "ARPOP_RREPLY";
    break;

  case ARPOP_InREQUEST:
    return "ARPOP_InREQUEST";
    break;

  case ARPOP_InREPLY:
    return "ARPOP_InREPLY";
    break;

  case ARPOP_NAK:
    return "ARPOP_NAK";
    break;

  default:
    return "UNKNOWN_ARP_OPCODE";
  }
}
//-----------------------------------------------------------------------------
unsigned short in_cksum(unsigned short *addr, int len)
//-----------------------------------------------------------------------------
{
  register int sum = 0;
  u_short answer = 0;
  register u_short *w = addr;
  register int nleft = len;

  /*
   * Our algorithm is simple, using a 32 bit accumulator (sum), we add
   * sequential 16 bit words to it, and at the end, fold back all the
   * carry bits from the top 16 bits into the lower 16 bits.
   */
  while (nleft > 1) {
    sum += *w++;
    nleft -= 2;
  }

  /* mop up an odd byte, if necessary */
  if (nleft == 1) {
    *(u_char *) (&answer) = *(u_char *) w;
    sum += answer;
  }

  /* add back carry outs from top 16 bits to low 16 bits */
  sum = (sum >> 16) + (sum & 0xffff);       /* add hi 16 to low 16 */
  sum += (sum >> 16);               /* add carry */
  answer = ~sum;              /* truncate to 16 bits */
  return (answer);
}
//-----------------------------------------------------------------------------
void sgi_send_arp_request(sgi_data_t *sgi_dataP, char* dst_ip_addrP)
//-----------------------------------------------------------------------------
{
  char                 buffer[100]; // ARP request is 60 bytes including ethernet
  struct arphdr_s    *arph   = (struct arphdr_s *)(buffer);
  struct hostent      *hp     = NULL;
  uint32_t             i;
  struct in_addr       ip_dst;
  struct ifreq         ifr;
  struct iovec         iov[2];

  sgi_dataP->eh.ether_dhost[0] = (u_int8_t)(0xFF);
  sgi_dataP->eh.ether_dhost[1] = (u_int8_t)(0xFF);
  sgi_dataP->eh.ether_dhost[2] = (u_int8_t)(0xFF);
  sgi_dataP->eh.ether_dhost[3] = (u_int8_t)(0xFF);
  sgi_dataP->eh.ether_dhost[4] = (u_int8_t)(0xFF);
  sgi_dataP->eh.ether_dhost[5] = (u_int8_t)(0xFF);

  // get IP address of the default virtual interface
  ifr.ifr_addr.sa_family = AF_INET;
  sprintf(ifr.ifr_name, "%s.%d",sgi_dataP->interface_name,SGI_MIN_EPS_BEARER_ID);

  if (ioctl(sgi_dataP->sd[0], SIOCGIFADDR, &ifr) != 0) {
    SGI_IF_ERROR("Error during IP ADDRESS RETRIEVAL ON SGI INTERFACE (%s:%d)\n",strerror(errno), errno);
    exit (-1);
  }

  if ((hp = gethostbyname(dst_ip_addrP)) == NULL) {
    SGI_IF_DEBUG("gethostbyname() is OK.\n");

    if ((ip_dst.s_addr = inet_addr(dst_ip_addrP)) == -1) {
      SGI_IF_ERROR("%s: Can't resolve, unknown host.\n", dst_ip_addrP);
      exit(1);
    }
  } else
    bcopy(hp->h_addr_list[0], &ip_dst.s_addr, hp->h_length);

  arph->ar_hrd = htons(ARPHRD_ETHER);
  arph->ar_pro = htons(ETH_P_IP);
  arph->ar_hln = ETH_ALEN;
  arph->ar_pln = 4;
  arph->ar_op  = htons(ARPOP_REQUEST);

  // sources
  memcpy(arph->ar_sha, sgi_dataP->eh.ether_shost, ETH_ALEN);

  i = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
  arph->ar_sip[3] = i >> 24;
  arph->ar_sip[2] = (i & 0x00FF0000) >> 16;
  arph->ar_sip[1] = (i & 0x0000FF00) >> 8;
  arph->ar_sip[0] = (i & 0x000000FF);

  // destinations
  memset(arph->ar_tha, 0, ETH_ALEN);

  i = ip_dst.s_addr;
  arph->ar_tip[3] = i >> 24;
  arph->ar_tip[2] = (i & 0x00FF0000) >> 16;
  arph->ar_tip[1] = (i & 0x0000FF00) >> 8;
  arph->ar_tip[0] = (i & 0x000000FF);
  // save ip address of router for comparing with ARP REPLY
  sgi_dataP->ipv4_addr_of_router = ip_dst.s_addr;

  iov[0].iov_base   = &sgi_dataP->eh;
  iov[0].iov_len    = sizeof(sgi_dataP->eh);
  iov[1].iov_base   = (void *)buffer;
  iov[1].iov_len    = sizeof (struct arphdr) + ETH_ALEN*2 + 4*2;
#ifdef VLAN8021Q
  sgi_data_p->eh.ether_vlan8021q.vlan_tpid = htons(0x8100);
  sgi_dataP->eh.ether_vlan8021q.vlan_tci  = htons(0x7000 | 0x0000 | SGI_MIN_EPS_BEARER_ID);
#endif
  sgi_dataP->eh.ether_type     = htons(ETH_P_ARP);

  sgi_print_hex_octets(iov[0].iov_base, iov[0].iov_len);
  sgi_print_hex_octets(iov[1].iov_base, iov[1].iov_len);

  if (writev(sgi_dataP->sd[0], (const struct iovec *)&iov, 2) < 0) {
    SGI_IF_ERROR("Error during send ARP to socket %d bearer id %d : (%s:%d)\n",
                 sgi_dataP->sd[0],
                 SGI_MIN_EPS_BEARER_ID,
                 strerror(errno),
                 errno);
    exit(1);
  }

}

