#include <stdio.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <pcap.h>
#include <linux/if_ether.h>
#include <netinet/ip_icmp.h>

#define DEFAULT_SNAPLEN 1500


char ebuf[PCAP_ERRBUF_SIZE];

void print_it(u_char *user, const struct pcap_pkthdr *h, const u_char *p){
  struct ethhdr	*eth = (struct ethhdr *)p;
  struct ip    	*ip;
  struct icmp	*icmp;
  unsigned char	*buff;
  int         	j,i = sizeof(struct ethhdr),icmp_size,k;

  printf("Total size (-14): %u\n",h->len-14);
  ip = (struct ip *)(p+i);
  buff = (unsigned char*)(p+i);
  i += (ip->ip_hl<<2);
  icmp = (struct icmp *)(p+i);
  printf("ip->ip_hl<<2:			%u\n",ip->ip_hl<<2);
  printf("ip->ip_v:			%#x\n",ip->ip_v);
  printf("ip->ip_tos:			%#x\n",ip->ip_tos);
  printf("htons(ip->ip_len):		%#u\n",htons(ip->ip_len));
  printf("htons(ip->ip_id):		%#x\n",htons(ip->ip_id));
  printf("htons(ip->ip_off):		%#x\n",htons(ip->ip_off));
  printf("ip->ip_ttl:			%#x\n",ip->ip_ttl);
  printf("ip->ip_p:			%#x\n",ip->ip_p);
  printf("htons(ip->ip_sum):		%#x\n",htons(ip->ip_sum));
  printf("htonl(ip->ip_src.s_addr):	%#x\n",htonl(ip->ip_src.s_addr));
  printf("htonl(ip->ip_dst.s_addr):	%#x\n",htonl(ip->ip_dst.s_addr));
  printf("\n"); 
  icmp_size = 4;
  printf("icmp->icmp_type		%#x (1 byte)\n",icmp->icmp_type);
  printf("icmp->icmp_code		%#x (1 byte)\n",icmp->icmp_code);
  printf("htons(icmp->icmp_cksum)	%#x (2 bytes)\n",htons(icmp->icmp_cksum));
  switch(icmp->icmp_type){
    case ICMP_ECHOREPLY:
      printf("\tICMP_ECHOREPLY:\n");
      printf("\thtons(icmp->icmp_id)	%#x (2 bytes)\n",htons(icmp->icmp_id));
      printf("\thtons(icmp->icmp_seq)	%#x (2 bytes)\n",htons(icmp->icmp_seq));
      icmp_size += 4;
    break;
    case ICMP_UNREACH:
      printf("\tICMP_UNREACH:");
      switch(icmp->icmp_code){
	case ICMP_UNREACH_NET: 
	  printf("bad net\n");
	break;
	case ICMP_UNREACH_HOST:
	  printf("bad host\n");
	break;
	case ICMP_UNREACH_PROTOCOL:
	  printf("bad protocol\n");
	break;
	case ICMP_UNREACH_PORT:
	  printf("bad port\n");
	break;
	case ICMP_UNREACH_NEEDFRAG:
	  printf("IP_DF caused drop\n");
	break;
	case ICMP_UNREACH_SRCFAIL:
	  printf("src route failed\n");
	break;
	case ICMP_UNREACH_NET_UNKNOWN:
	  printf("unknown net\n");
	break;
	case ICMP_UNREACH_HOST_UNKNOWN:
	  printf("unknown host\n");
	break;
	case ICMP_UNREACH_ISOLATED:
	  printf("src host isolated\n");
	break;
	case ICMP_UNREACH_NET_PROHIB:
	  printf("net denied\n");
	break;
	case ICMP_UNREACH_HOST_PROHIB:
	  printf("host denied\n");
	break;
	case ICMP_UNREACH_TOSNET:
	  printf("bad tos for net\n");
	break;
        case ICMP_UNREACH_TOSHOST:
	  printf("bad tos for host\n");
        break;
        case ICMP_UNREACH_FILTER_PROHIB:
	  printf("admin prohib\n");
        break;
        case ICMP_UNREACH_HOST_PRECEDENCE:
	  printf("host prec vio.\n");
        break;
        case ICMP_UNREACH_PRECEDENCE_CUTOFF:
	  printf("prec cutoff\n");
        break;
      }
      printf("\thtonl(icmp->icmp_void)	%#x (4 bytes)\n",htonl(icmp->icmp_void));
      printf("\tIP header(incl. opts) + 8 bytes of original IP datagram data (%u bytes)\n",ip->ip_hl<<2+8);
      icmp_size += ip->ip_hl<<2 + 8 + 4;
    break;
    case ICMP_SOURCEQUENCH:
      printf("\tICMP_SOURCEQUENCH:\n");
      printf("\thtonl(icmp->icmp_void)  %#x (4 bytes)\n",htonl(icmp->icmp_void));
      printf("\tIP header(incl. opts) + 8 bytes of original IP datagram data (%u bytes)\n",ip->ip_hl<<2+8);
      icmp_size += ip->ip_hl<<2 + 8 + 4;
    break;
    case ICMP_REDIRECT:
      printf("\tICMP_REDIRECT:");
      switch(icmp->icmp_code){
        case ICMP_REDIRECT_NET:
          printf("for network\n");
	break;
	case ICMP_REDIRECT_HOST:
	  printf("for host\n");
	break;
	case ICMP_REDIRECT_TOSNET:
	  printf("for tos and net\n");
	break;
	case ICMP_REDIRECT_TOSHOST:
	  printf("for tos and host\n");
	break;
      }
      printf("\thtonl(icmp->icmp_gwaddr.s_addr)	%#x (4 bytes)\n",htonl(icmp->icmp_gwaddr.s_addr));
      printf("\tIP header(incl. opts) + 8 bytes of original IP datagram data (%u bytes)\n",ip->ip_hl<<2+8);
      icmp_size += ip->ip_hl<<2 + 8 + 4;
    break;
    case ICMP_ECHO:
      printf("\tICMP_ECHO:\n");
      printf("\thtons(icmp->icmp_id)    %#x (2 bytes)\n",htons(icmp->icmp_id));
      printf("\thtons(icmp->icmp_seq)   %#x (2 bytes)\n",htons(icmp->icmp_seq));
      icmp_size += 4;      
    break;
    case ICMP_ROUTERADVERT:
      printf("\tICMP_ROUTERADVERT:\n");
      printf("\ticmp->icmp_num_addrs		%#x (1 byte)\n",icmp->icmp_num_addrs);
      printf("\ticmp->icmp_wpa			%#x (1 byte)\n",icmp->icmp_wpa);
      printf("\thtons(icmp->icmp_lifetime)	%#x (2 bytes)\n",htons(icmp->icmp_lifetime));
      icmp_size += 4 + 8*icmp->icmp_num_addrs;
    break;
    case ICMP_ROUTERSOLICIT:
      printf("\tICMP_ROUTERSOLICIT\n");
      printf("\thtonl(icmp->icmp_void)  %#x (4 bytes)\n",htonl(icmp->icmp_void));
      icmp_size += 4;
    break;
    default:
      printf("Any other...\n");
  }
  printf("ICMP size:	%u\n",icmp_size);
  
  for(j=0;j<htons(ip->ip_len);j+=2){
    printf("%c%.2x%.2x",j%16?' ':'\n',buff[j],buff[j+1]);
  }
  printf("\n\n");
  fflush(stdout);
}

int main(){
  int            snaplen = DEFAULT_SNAPLEN;
  char           *device = pcap_lookupdev(ebuf);
  pcap_t         *pd     = pcap_open_live("eth0", snaplen, 1, 1000, ebuf);
  bpf_u_int32    localnet, netmask;
  struct         bpf_program fcode;
  pcap_handler   printer;
  
  pcap_lookupnet("eth0", &localnet, &netmask, ebuf);

  printf("\033[0;37m\n"); 
 
  pcap_compile(pd, &fcode, "ip and icmp and dst 10.1.1.2", 1, netmask);

  pcap_setfilter(pd, &fcode);

  printer = print_it;

  pcap_loop(pd, 0, printer, 0);

  fflush(stdout);
  return 0;
}
