/* Copyright (C) 2003 Renaud Deraison
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* XXX: There once was a BPF sharing feature with the same API
 * as the methods below, but trying to share BPF among the daemon
 * processes. What remains is a thin abstraction of the pcap API.
 * Eventually it needs to be analysed whether this makes sense
 * or can further be simplified.
 */

#include <pcap.h>

#undef DEBUG_FORWARD
#undef DEBUG
#undef DEBUG_HIGH
#define NUM_CLIENTS 128

static pcap_t * pcaps[NUM_CLIENTS];

	

int bpf_open_live(char * iface, char * filter)
{
 char errbuf[PCAP_ERRBUF_SIZE];
 pcap_t * ret;
 bpf_u_int32 netmask, network;
 struct bpf_program filter_prog;
 int i;
 
 for(i=0;i< NUM_CLIENTS && pcaps[i];i++);

 if(pcaps[i])
 {
  printf("no free pcap\n");
  return -1;
 }
  
 
 if(iface == NULL)
  iface = pcap_lookupdev(errbuf);
 
 ret = pcap_open_live(iface, 1500, 0, 1, errbuf);
 if(ret == NULL)
 {
    printf("%s\n", errbuf);	 
  return -1;
 }

 if(pcap_lookupnet(iface, &network, &netmask, 0) < 0)
 { 
   printf("pcap_lookupnet failed\n");
   pcap_close(ret);
   return -1;
 }
 
 if(pcap_compile(ret, &filter_prog, filter, 1, netmask) < 0)
 {
  pcap_perror(ret, "pcap_compile");
  pcap_close(ret);
  return -1;
 }

 if (pcap_setnonblock(ret,1,NULL)==-1) {
	pcap_perror(ret,"pcap_setnonblock");
	printf("call to pcap_setnonblock failed, some plugins/scripts will hang/freeze. Upgrade your version of libcap!\n");	
 }
 
 if(pcap_setfilter(ret, &filter_prog) < 0) 
 {
  pcap_perror(ret, "pcap_setfilter\n");
  pcap_close(ret);
  return -1;
 }
 pcaps[i] = ret;
 return i;
}



u_char* bpf_next_tv(int bpf, int * caplen, struct timeval * tv)
{
  u_char * p = NULL;
  struct pcap_pkthdr head;
  struct timeval timeout, now;

  gettimeofday(&timeout, NULL);
  timeout.tv_sec += tv->tv_sec;
  timeout.tv_usec += tv->tv_usec;
  while ( timeout.tv_usec >= 1000000 ) {
        timeout.tv_sec ++;
        timeout.tv_usec -= 1000000;
  }
  
 do {
  p = (u_char*)pcap_next(pcaps[bpf], &head);
  *caplen  = head.caplen;
  if ( p != NULL ) break;
  gettimeofday(&now, NULL);
 } while ( !((now.tv_sec > timeout.tv_sec) ||
             (now.tv_sec == timeout.tv_sec && now.tv_usec >= timeout.tv_usec ) ));


 return p;
}


u_char* bpf_next(int bpf, int * caplen)
{
 struct timeval tv = {0, 100000};
 
 return bpf_next_tv(bpf, caplen, &tv);
}


int bpf_datalink(int bpf)
{
 return pcap_datalink(pcaps[bpf]);
}


void bpf_close(int bpf)
{
 pcap_close(pcaps[bpf]);
 pcaps[bpf] = NULL;
}


int bpf_server()
{
  return 0;
}
