#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 "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;

  if (dataP == NULL) {
    return;
  }


  SGI_IF_DEBUG("------+-------------------------------------------------|\n");
  SGI_IF_DEBUG("      |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |\n");
  SGI_IF_DEBUG("------+-------------------------------------------------|\n");
  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", 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",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);
    }

}

