/**********************************************************************
* 
* Adopted from Martin Casado, 
*    http://yuba.stanford.edu/~casado/pcap/section3.html 
*
* See also: http://www.devdungeon.com/content/using-libpcap-c
*
**********************************************************************/

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>

// use these to further dissect packets
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h> 

// global: datalink type and header size
int dltype;
int dlt_size;

struct my_pcap_context {
     int cnt;
};

void callback(u_char *, const struct pcap_pkthdr*, const u_char*);

void callback(u_char *user,                  // pass data between the calls with this 
              const struct pcap_pkthdr* pkthdr, // pcap header, metadata about the packet
              const u_char* packet)             // bytes on the wire, starting with Layer2 (e.g., Ethernet)
{
     int i;
     // struct ether_header *eptr;  /* net/ethernet.h            */

     // IP header (or another layer 3 protocol header, e.g., ARP) 
     //   will be found at the offset dlt_size .

     printf("\nFrame %d: %d bytes on the wire, %d captured\n", ((struct my_pcap_context*) user)->cnt++, 
	    pkthdr->len, pkthdr->caplen);
     for (i = 0; i < (bpf_u_int32) pkthdr->caplen; i++) {
          printf("%02X%s", (uint8_t)packet[i], (i + 1)%16 ? " " : "\n");
     }
     printf("\n");
}

int main(int argc,char **argv)
{ 
     char *dev; 
     char errbuf[PCAP_ERRBUF_SIZE];
     pcap_t* cap;
     struct bpf_program fp;      /* hold compiled program     */
     bpf_u_int32 maskp;          /* subnet mask               */
     bpf_u_int32 netp;           /* ip                        */
     
     if(argc != 3){ 
          fprintf(stdout,"Usage: %s interface \"filter program\"\n", argv[0]);
          return 0;
     }

     dev = argv[1];
     
     /* open device for reading this time lets set it in promiscuous
      * mode so we can monitor traffic to another machine             */
     cap = pcap_open_live(dev, IP_MAXPACKET, // snaplen, truncate packets at this size (0 will do)
                            1, 1000, errbuf);    // promiscuous mode? , timeout 
     if(cap == NULL){
          printf("pcap_open_live(): %s\n", errbuf); 
          exit(1); 
     }
     
     /* ask pcap for the network address and mask of the device */
     if( pcap_lookupnet(dev, &netp, &maskp, errbuf) < 0 ){
          printf("pcap_open_live(): %s\n", errbuf); 
          exit(1); 
     }

     // Datalink is important because the packet will start with a datalink-specific
     //   header, which we must skip to get to IP. But how many bytes to skip?
     //   Different DLTs have different lengths. We can either hard-code them, 
     //   or try setting DLT_RAW, to get rid of the datalink header entirely.
     //   This will lose us info such as on which intreface the packet arrived,
     //   and from what MAC address. --SB: RAW does not work on "any".
     dltype = pcap_datalink(cap);
     printf("Datalink type (see DLT_* in pcap/bpf.h and 'man pcap-linktype'): %d\n", dltype);
     switch(dltype){  // this seems like something PCAP would have a function for...
     case PCAP_ERROR_NOT_ACTIVATED:
          fprintf(stderr, "Failed to get data link type, bailing.\n");
          exit(1);
     case DLT_RAW:
          dlt_size = 0;
          break;
     case DLT_LINUX_SLL:  // "any" for device will give you this
          dlt_size = 16;
          break;
     case DLT_EN10MB:
          dlt_size = 14;
          break;
     default:
          fprintf(stderr, "Unknown data link type %d, bailing.\n", dltype );
          exit(1);
     }

     /* Lets try and compile the program.. non-optimized */
     /*  Instead of netp, you can use PCAP_NETMASK_UNKNOWN if you don't need
	 to use the broadcast-related filtering; this saves a call to pcap_lookupnet() above. */
     if(pcap_compile(cap, &fp, argv[2], 0, netp) == -1){
          fprintf(stderr, "Error calling pcap_compile (filter: %s)\n", argv[2]); 
          exit(1); 
     }
     
     /* set the compiled program as the filter */
     if(pcap_setfilter(cap, &fp) == -1){
          fprintf(stderr, "Error setting filter\n"); 
          exit(1); 
     }
     
     struct my_pcap_context mydata = { .cnt = 0 };

     /* ... and loop */ 
     pcap_loop(cap, -1, callback, (u_char*) &mydata); //cast to avoid type warning
     
     return 0;
}
