//
// $Id: Pcap.cc,v 1.1.1.1 2000/03/10 16:32:19 engin Exp $
//
// Author(s): Cengiz Alaettinoglu

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(HAVE_PCAP_H)

#include "util/Types.hh"
#include "util/Trail.hh"
#include "util/Handler.hh"
#include "util/Buffer.hh"
#include "sys/File.hh"
#include "sys/Pipe.hh"
#include "sys/Time.hh"
#include "sched/Timer.hh"
#include "sched/Dispatcher.hh"

#include "network/Pcap.hh"
#include "network/Headers.hh"
#include "network/Network.hh"

extern "C" {
#include <sys/socket.h>
#include <net/if.h>
    //#include <net/if_arp.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/in_systm.h>
    //#include <netinet/ip.h>

#include <pcap.h>

int     pcap_read(pcap_t *, int cnt, pcap_handler, u_char *);
}

static int tuba=0, idrp=0; //some pcaps want these flags declared

// Locals
static TraceCode	tracePcap("network");
const int DefaultSnapLen = 68;

static void pcapReadHandler(void* pcap, void*) {
   ((Pcap *) pcap)->issueRead();
}

static void pcapProcessHandler(u_char *pcap, 
			       const struct pcap_pkthdr *h, 
			       const u_char *p) {
    u_int caplen = h->caplen;
    u_int length = h->len;
    u_short pay_type;
    
    switch (((Pcap*)pcap)->datalink_) {
    case DLT_EN10MB :
    case DLT_IEEE802 :
	if (h->caplen < sizeof(struct ether_header)) {
	    TRACE(tracePcap, "ethernet header not found\n");
	    return;
	}
        length-= sizeof(struct ether_header);
        caplen-= sizeof(struct ether_header);
	pay_type = ntohs(((struct ether_header *)p)->ether_type);
	p += sizeof(struct ether_header);
	break;
    case DLT_ATM_RFC1483 :
 	if (h->caplen < 8) {
	    TRACE(tracePcap, "ATM header not found\n");
	    return;
	}
        if (p[4] == 0xaa || p[5] == 0xaa || p[6] == 0x03) {
	    /* if first 4 bytes are cookie/vpci */
	    p+= 4;
	    length-= 4;
	    caplen-= 4;
	} else if (p[0] != 0xaa || p[1] != 0xaa || p[2] != 0x03) {
	    /*XXX assume 802.6 MAC header from fore driver */
	    p+= 20;
	    length-= 20;
	    caplen-= 20;
        }
	pay_type = p[6] << 8 | p[7];
        length-= 8;
        caplen-= 8;
        p+= 8;
	break;
    case DLT_SLIP :
	//case DLT_SLIP_BSDOS :
    case DLT_PPP :
	//case DLT_PPP_BSDOS :
    case DLT_FDDI :
    case DLT_NULL :
	//case DLT_RAW :
	ERROR("unsupported link layer %d\n", ((Pcap*)pcap)->datalink_);
	return;
    }
    if (pay_type != ETHERTYPE_IP)
	return;
    if (caplen < sizeof(IP)) {
	TRACE(tracePcap, "ip header not found\n");
	return;
    }
    IP *ip= (IP*)p;
    ip->ntoh();

    Address src(ip->source);
    Address dst(ip->destination);

   ((Pcap*) pcap)->receive(src, dst, ip->protocol);
}

Pcap::Pcap(char *filter, char *device) : ListNode() {
   int snaplen = DefaultSnapLen;
   int promiscuous = 1;
   char ebuf[PCAP_ERRBUF_SIZE];
   bpf_u_int32 localnet, netmask;
   struct bpf_program fcode;
   int Oflag = 1;			/* run filter code optimizer */
   int i;

   if (device == NULL) {
      device = pcap_lookupdev(ebuf);
      if (device == NULL)
	 FATAL("%s\n", ebuf);
   }

   pcap_descriptor = pcap_open_live(device, snaplen, promiscuous, 1000, ebuf);
   if (pcap_descriptor == NULL)
      FATAL("%s\n", ebuf);

   datalink_= pcap_datalink(pcap_descriptor);

   i = pcap_snapshot(pcap_descriptor);
   if (snaplen < i) {
      ERROR("snaplen raised from %d to %d", snaplen, i);
      snaplen = i;
   }

   if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) {
      localnet = 0;
      netmask = 0;
      ERROR("%s\n", ebuf);
   }

   if (pcap_compile(pcap_descriptor, &fcode, filter, Oflag, netmask) < 0)
      FATAL("%s\n", pcap_geterr(pcap_descriptor));

   if (pcap_setfilter(pcap_descriptor, &fcode) < 0)
      FATAL("%s\n", pcap_geterr(pcap_descriptor));

   Handler rH(pcapReadHandler, this);
   Handler nH((CallBackFunc)NULL, (void *)NULL);
   // the pcap_t points to memory where the first word is the file descriptor 
   file = new File(* (int *) pcap_descriptor, FileModeReadOnly, rH, nH);
}

Pcap::~Pcap() {
   pcap_close(pcap_descriptor);
   delete file;
}

void Pcap::issueRead() {
   if (pcap_read(pcap_descriptor, 
		 1, 
		 pcapProcessHandler, 
		 (u_char *) this) < 0)
      TRACE(tracePcap, "pcap: %s\n", pcap_geterr(pcap_descriptor));
}

//  Copyright (c) 1994 by the University of Southern California.
//  All rights reserved.
//
//  Permission to use, copy, modify, and distribute this software and
//  its documentation in source and binary forms for lawful
//  non-commercial purposes and without fee is hereby granted, provided
//  that the above copyright notice appear in all copies and that both
//  the copyright notice and this permission notice appear in supporting
//  documentation, and that any documentation, advertising materials,
//  and other materials related to such distribution and use acknowledge
//  that the software was developed by the University of Southern
//  California and/or Information Sciences Institute.
//  The name of the University of Southern California may not
//  be used to endorse or promote products derived from this software
//  without specific prior written permission.
//
//  THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
//  ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
//  PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
//  INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
//  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND 
//  NON-INFRINGEMENT.
//
//  IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
//  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
//  TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
//  THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
//  Questions concerning this software should be directed to 
//  scan@isi.edu.
//


#endif HAVE_PCAP

