#define _GNU_SOURCE
#include <netinet/ether.h> 
#include <netpacket/packet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>

#define BUFFSIZE 65536

#define KILO 1024
#define MEGA KILO*1024
#define GIGA MEGA*1024
#define TERA GIGA*1024

unsigned char	buff[BUFFSIZE];
int		packet_socket;
int		if1,if2;
fd_set		rfds;
int		turnspassed = 0;
unsigned long int	bytespassed = 0;
unsigned long int	packetspassed = 0;

int getifindexfromdevice(char *ifname)
{
    int s;
    struct sockaddr_ll sa;
    struct ifreq ifr;
    
    memset(&sa, 0, sizeof(sa));
    memset(&ifr, 0, sizeof(ifr));
    
    if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
		printf("Socket creation error.\n");
		exit(1);
    }

    strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
    if (ioctl(s, SIOCGIFINDEX, &ifr) < 0){
	printf("Error getting interface number.\n");
	exit(2);
    }

    close(s);
    return(ifr.ifr_ifindex);
    
}

void setdesiredsockopts(int socktoset)
{
	int sockopt = 1;

	if ((setsockopt(socktoset,SOL_SOCKET,SO_DONTROUTE,&sockopt,sizeof(int)))!=0){
	    printf("setsockopt error\n");
	    exit(1);
	}

	if ((setsockopt(socktoset,SOL_SOCKET,SO_REUSEADDR,&sockopt,sizeof(int)))!=0){
	    printf("setsockopt error\n");
	    exit(1);
	}

	if ((setsockopt(socktoset,SOL_SOCKET,SO_OOBINLINE,&sockopt,sizeof(int)))!=0){
	    printf("setsockopt error\n");
	    exit(1);
	}
}

int createreadsocket()
{
	int s;
	if ((s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0){
		printf("Socket creation error.\n");
		exit(3);
	}
	
//	setdesiredsockopts(s);
	return (s);	
}

int getotherif(int oneif)
{
    if (oneif == if1){
	return (if2);
    }else if (oneif == if2){
	return (if1);
    }
    return -1;
}

void recvandsend()
{
    struct sockaddr_ll  sa;
    socklen_t  sllen;
    size_t recvd;

    memset(&sa, 0, sizeof(sa));

    recvd = recvfrom(packet_socket, buff, BUFFSIZE, 0, (struct sockaddr *)&sa, &sllen);
    if (recvd == -1){
	printf("receive error\n");
//	exit(2);
    }
    
    if((sa.sll_ifindex != if1) && (sa.sll_ifindex != if2)){
//	printf("Packet came from ifnum: %d, instead of %d or %d.\n",sa.sll_ifindex,if1,if2);
	return;
    }
    
    if((sa.sll_pkttype != PACKET_OTHERHOST) && (sa.sll_pkttype != PACKET_BROADCAST) && (sa.sll_pkttype != PACKET_MULTICAST)){
//	printf("Packet type was: %d, instead of %d or %d or %d.\n",sa.sll_pkttype,PACKET_OTHERHOST,PACKET_BROADCAST,PACKET_MULTICAST);
	return;
    } 
    
    sa.sll_ifindex = getotherif(sa.sll_ifindex);

    if(recvd != (sendto(packet_socket,buff,recvd,0,(struct sockaddr *)&sa,sizeof(sa)))){
	printf("send error");
//	exit(1);
    }else{
//	printf("packet written to %d, size: %d bytes.\n",sa.sll_ifindex,recvd);
	bytespassed += recvd;
	packetspassed++;
    }    
    return;
}

void processoneturn()
{
    int retval;
    fd_set  rfds;
    struct timeval  tv;

    tv.tv_sec = 2;
    tv.tv_usec = 0;

/*    turnspassed++;
    
    if(turnspassed == 100){
	printf("100 select passed\n");
	turnspassed=0;
    }
*/    
    if(bytespassed >= 1*GIGA){
	printf("1 Gbyte passed\n");
	bytespassed = 0;
    }

    if(packetspassed >= 100000){
	printf("100000 packets passed\n");
	packetspassed = 0;
    }
    
    FD_ZERO(&rfds);
    FD_SET(packet_socket,&rfds);

    retval = select(packet_socket+1,&rfds,NULL,NULL,&tv);

    if (retval < 0){
        printf("select() error\n");
//        exit(1);
    }else if (retval == 0){
	//do nothing
    }else {
	//read
	if(FD_ISSET(packet_socket,&rfds)){
//	    printf("some packet hmm...\n");
	    recvandsend();
	}
    }
}

int main(int argc, char **argv)
{
	char *dev1, *dev2;

	if(argc != 3){
	    printf("Usage: %s dev1 dev2\n",program_invocation_short_name);
	    exit(1);
	}
	
	dev1 = argv[1];
	dev2 = argv[2];
	if1 = getifindexfromdevice(dev1);
	if2 = getifindexfromdevice(dev2);

	printf("Bridging between %s(ifnum:%d) and %s(ifnum:%d).\n",dev1,if1,dev2,if2);

	packet_socket = createreadsocket();
	
	while(1){
	    processoneturn();
	}
	return 0;
}

