/* 
 * 
 * socket.c -- Traffic Squeezer socket wrapper code
 *
 * Author: Kiran K (2006-2008)
 *         kiran_cyberpro at yahoo dot com
 *
 *
 * Traffic Squeezer 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 2, or (at your option) any
 * later version.
 *
 * Traffic Squeezer 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 GNU Zebra; see the file COPYING.  If not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <malloc.h>
#include <net/ethernet.h>
#include <netpacket/packet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
#include <sys/types.h>

#include "socket.h"
#include "common.h"

static int readable_or_timeout (int sd, int timeout_in_seconds)
{
  fd_set         read_sd_set;  /* set of socket descriptors that we
                                  expect to read from */
  int            status;
  struct timeval tv;           /* select() needs the timeout in this type of
                                  structure */
  FD_ZERO (&read_sd_set);
  FD_SET  (sd, &read_sd_set);

  tv.tv_sec = timeout_in_seconds;
  tv.tv_usec = 0;  /* zero microseconds. */

  status = select (sd+1,         /* highest-numbered descriptor + 1 */
                   &read_sd_set, /* descriptor set for reading */
                   NULL,         /* descriptor set for writing (none) */
                   NULL,         /* descriptor set for exceptions (none) */
                   &tv);         /* time out */
  return (status);
}



int tx_frame(int sock_fd, BYTE *buf, int len)
{
    fd_set readset;
    int  result = 0;
    int bytes_sent;
    BYTE *frameBuff = (BYTE *)buf;
    BYTE *frame;
#if 0
    printf("\nPacket to be send \n");
    for (bytes_sent =0; bytes_sent < len; bytes_sent++)
    {
        printf("0x%02x ", *(buf + bytes_sent) );
	if(bytes_sent%16 == 0)
		printf("\n");
	else if(bytes_sent%8 == 0)
		printf(" ");
    }
    printf("\npacket is ready to be sent\n");
#endif

    frameBuff = (BYTE *)buf;

    //if (len < 60) //Add Padding
    //{
    //    frame = (BYTE *) malloc(60);
    //    memset(frame, 0, 60);
    //   memcpy(frame, frameBuff, len);
    //    write(sock_fd, frame, 60);
    //    free(frame);
    //}
    //else
    {
        write(sock_fd, frameBuff, len);
    }

    return TRUE;
}

int rx_frame(int sock_fd, BYTE *buf, size_t *len)
{
    fd_set readset;
    int  result = 0;
    unsigned char pktBuff[1500];
    int pktLen = 1500;
    int status;
    const int timeout=1;

    status = readable_or_timeout (sock_fd, timeout);
    if      (status <= 0) {return FALSE;}


    memset(pktBuff,0,1500);
    result = recv(sock_fd, pktBuff, pktLen, 0);
	 
    if (result > -1)
    {
        memcpy(buf,pktBuff,result);
        *len = result;
        return TRUE;
    }

   return FALSE;
}

int create_sockets(int *ingress_port_fd, int *egress_port_fd, char *ingress_prt, \
                                char *egress_prt, BYTE *ingress_mac, BYTE *egress_mac)
{
	if(!strcmp(ingress_prt, egress_prt)) //ingress and egress ports are same :( [Bug fixed: Kiran, 13_May_07]
		return FALSE;

	*ingress_port_fd = create_socket(ingress_prt, ingress_mac);
        *egress_port_fd = create_socket(egress_prt, egress_mac);

        if( !(*ingress_port_fd) )
                return FALSE;

        if( !(*egress_port_fd) )
                return FALSE;

        return TRUE;
}

int create_socket(char *device, BYTE *mac)
{
        int sock_fd;
        struct ifreq ifr;
        struct sockaddr_ll sll;
        
	memset(&ifr, 0, sizeof(ifr));
        memset(&sll, 0, sizeof(sll));

        sock_fd = socket (PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
        if (sock_fd == 0)
        {	
		printf(" TRFSQRERR: socket creation for device: %s\n", device);
                return 0;
        }

        strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
        if (ioctl(sock_fd, SIOCGIFINDEX, &ifr) == -1)
        {
		printf(" TRFSQERR: ioctl failed for device: %s\n", device);
                return 0;
        }
        sll.sll_family      = AF_PACKET;
        sll.sll_ifindex     = ifr.ifr_ifindex;
        sll.sll_protocol    = htons(ETH_P_ALL);
        if (bind(sock_fd, (struct sockaddr *) &sll, sizeof(sll)) == -1)
        {
		printf(" TRFSQERR: bind failed for device: %s\n", device);
                return 0;
        }

        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
        if (ioctl(sock_fd, SIOCGIFHWADDR, &ifr) == -1)
        {
		printf(" TRFSQERR: ioctl failed for device: %s\n", device);
                return 0;
        }
        memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
	/* set promisc mode: Added: 6-May-07: Kiran */
	ifr.ifr_flags |= IFF_PROMISC;
	if(ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1)
	{
		return 0;
	}

    return sock_fd;
}

int close_socket(int sock_fd)
{

	close(sock_fd);
	return 1;
}


