/* 
 *  Ths code in this file is part of tcptrack. For more information see
 *    http://www.rhythm.cx/~steve/devel/tcptrack
 *
 *     Copyright (C) Steve Benson - 2003
 *
 *  tcptrack 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.
 *   
 *  tcptrack 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 Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  
 */
#define _BSD_SOURCE 1
#define _REENTRANT
#include <queue>
#include <time.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <assert.h>
#include "headers.h"
#include "TCContainer.h"
#include "PacketBuffer.h"
#include "defs.h"

PacketBuffer::PacketBuffer(TCContainer *nc)
{
	inq = &pq1;
	outq = &pq2;
	c=nc;

	//
	// Start up maintenence thread
	//
	int rc = pthread_create(&maint_thread_tid,NULL,pbmaint_thread_func,this);
	if( rc )
	{
		perror("pthread_create");
		exit(1); 
	}

}

void PacketBuffer::pushPacket( const u_char * packet )
{
	assert(packet!=NULL);
	
	const struct sniff_ethernet *ethernet; /* The ethernet header */
	const struct sniff_ip *ip; /* The IP header */
	const struct sniff_tcp *tcp; /* The TCP header */

	//
	// parse the headers off the packet 
	// make sure the packet is a TCP/IP packet
	//
	ethernet = (struct sniff_ethernet*)(packet);
	if( ethernet->ether_type != 8 )  // TODO: is this defined somewhere?
		return;

	ip = (struct sniff_ip*)(packet + ENET_HEADER_LEN);
	if( ip->ip_p != IPPROTO_TCP ) 
		return;

	tcp = (struct sniff_tcp*)(packet + ENET_HEADER_LEN + IP_HEADER_LEN);

	
	//
	// copy the packet IP & TCP headers (we're only interested in those)
	// into a new buffer. pcap reuses the current one.
	//
	
	int newpkt_len = IP_HEADER_LEN+TCP_HEADER_LEN;
	assert(newpkt_len>=IP_HEADER_LEN); // make sure we at least have an ip hdr

	// this memory is freed in TCContainer
	const u_char *newpkt = (const u_char *) malloc(newpkt_len); 
	assert(newpkt!=NULL);

	memcpy((void *)newpkt,packet+ENET_HEADER_LEN,newpkt_len);
	
	pthread_mutex_lock(&inq_lock);
	inq->push(newpkt);
	pthread_mutex_unlock(&inq_lock);
}

void PacketBuffer::maint_thread_run()
{
	struct timespec t;
	const u_char *packet;
	
	while(true)
	{
		t.tv_sec=0;
		t.tv_nsec=PKTBUF_PROC;
		nanosleep(&t,NULL);
		
		// swap in & out queues
		// this allows the input queue to be unlocked ASAP so the Sniffer
		// won't be delayed for too long.
		pthread_mutex_lock(&inq_lock);
		if( inq == &pq1 )
		{
			inq = &pq2;
			outq = &pq1;
		}
		else
		{
			inq = &pq1;
			outq = &pq2;
		}
		pthread_mutex_unlock(&inq_lock);
		
		// process outq
		packet=NULL;
		while( ! outq->empty() )
		{
			packet=NULL;
			packet=outq->front();
			c->processPacket(packet);
			outq->pop();
		}
	}

}

////////////////////////

void *pbmaint_thread_func( void *arg )
{
	PacketBuffer *pb = (PacketBuffer *) arg;
	pb->maint_thread_run();
	return NULL;
}

